mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: SQL store split for annotations (#55089)
* Chore: SQL store split for annotations * Apply suggestion from code review
This commit is contained in:
parent
469f915b8c
commit
754eea20b3
@ -63,9 +63,7 @@ func (hs *HTTPServer) GetAnnotations(c *models.ReqContext) response.Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := annotations.GetRepository()
|
items, err := hs.annotationsRepo.Find(c.Req.Context(), query)
|
||||||
|
|
||||||
items, err := repo.Find(c.Req.Context(), query)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Failed to get annotations", err)
|
return response.Error(500, "Failed to get annotations", err)
|
||||||
}
|
}
|
||||||
@ -135,8 +133,6 @@ func (hs *HTTPServer) PostAnnotation(c *models.ReqContext) response.Response {
|
|||||||
return dashboardGuardianResponse(err)
|
return dashboardGuardianResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := annotations.GetRepository()
|
|
||||||
|
|
||||||
if cmd.Text == "" {
|
if cmd.Text == "" {
|
||||||
err := &AnnotationError{"text field should not be empty"}
|
err := &AnnotationError{"text field should not be empty"}
|
||||||
return response.Error(400, "Failed to save annotation", err)
|
return response.Error(400, "Failed to save annotation", err)
|
||||||
@ -154,7 +150,7 @@ func (hs *HTTPServer) PostAnnotation(c *models.ReqContext) response.Response {
|
|||||||
Tags: cmd.Tags,
|
Tags: cmd.Tags,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := repo.Save(&item); err != nil {
|
if err := hs.annotationsRepo.Save(c.Req.Context(), &item); err != nil {
|
||||||
if errors.Is(err, annotations.ErrTimerangeMissing) {
|
if errors.Is(err, annotations.ErrTimerangeMissing) {
|
||||||
return response.Error(400, "Failed to save annotation", err)
|
return response.Error(400, "Failed to save annotation", err)
|
||||||
}
|
}
|
||||||
@ -194,8 +190,6 @@ func (hs *HTTPServer) PostGraphiteAnnotation(c *models.ReqContext) response.Resp
|
|||||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||||
}
|
}
|
||||||
repo := annotations.GetRepository()
|
|
||||||
|
|
||||||
if cmd.What == "" {
|
if cmd.What == "" {
|
||||||
err := &AnnotationError{"what field should not be empty"}
|
err := &AnnotationError{"what field should not be empty"}
|
||||||
return response.Error(400, "Failed to save Graphite annotation", err)
|
return response.Error(400, "Failed to save Graphite annotation", err)
|
||||||
@ -234,7 +228,7 @@ func (hs *HTTPServer) PostGraphiteAnnotation(c *models.ReqContext) response.Resp
|
|||||||
Tags: tagsArray,
|
Tags: tagsArray,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := repo.Save(&item); err != nil {
|
if err := hs.annotationsRepo.Save(context.Background(), &item); err != nil {
|
||||||
return response.Error(500, "Failed to save Graphite annotation", err)
|
return response.Error(500, "Failed to save Graphite annotation", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,9 +261,7 @@ func (hs *HTTPServer) UpdateAnnotation(c *models.ReqContext) response.Response {
|
|||||||
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
|
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := annotations.GetRepository()
|
annotation, resp := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, annotationID, c.SignedInUser)
|
||||||
|
|
||||||
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
|
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
@ -288,7 +280,7 @@ func (hs *HTTPServer) UpdateAnnotation(c *models.ReqContext) response.Response {
|
|||||||
Tags: cmd.Tags,
|
Tags: cmd.Tags,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := repo.Update(c.Req.Context(), &item); err != nil {
|
if err := hs.annotationsRepo.Update(c.Req.Context(), &item); err != nil {
|
||||||
return response.Error(500, "Failed to update annotation", err)
|
return response.Error(500, "Failed to update annotation", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,9 +311,7 @@ func (hs *HTTPServer) PatchAnnotation(c *models.ReqContext) response.Response {
|
|||||||
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
|
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := annotations.GetRepository()
|
annotation, resp := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, annotationID, c.SignedInUser)
|
||||||
|
|
||||||
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
|
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
@ -356,7 +346,7 @@ func (hs *HTTPServer) PatchAnnotation(c *models.ReqContext) response.Response {
|
|||||||
existing.EpochEnd = cmd.TimeEnd
|
existing.EpochEnd = cmd.TimeEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := repo.Update(c.Req.Context(), &existing); err != nil {
|
if err := hs.annotationsRepo.Update(c.Req.Context(), &existing); err != nil {
|
||||||
return response.Error(500, "Failed to update annotation", err)
|
return response.Error(500, "Failed to update annotation", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +381,6 @@ func (hs *HTTPServer) MassDeleteAnnotations(c *models.ReqContext) response.Respo
|
|||||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := annotations.GetRepository()
|
|
||||||
var deleteParams *annotations.DeleteParams
|
var deleteParams *annotations.DeleteParams
|
||||||
|
|
||||||
// validations only for RBAC. A user can mass delete all annotations in a (dashboard + panel) or a specific annotation
|
// validations only for RBAC. A user can mass delete all annotations in a (dashboard + panel) or a specific annotation
|
||||||
@ -400,7 +389,7 @@ func (hs *HTTPServer) MassDeleteAnnotations(c *models.ReqContext) response.Respo
|
|||||||
var dashboardId int64
|
var dashboardId int64
|
||||||
|
|
||||||
if cmd.AnnotationId != 0 {
|
if cmd.AnnotationId != 0 {
|
||||||
annotation, respErr := findAnnotationByID(c.Req.Context(), repo, cmd.AnnotationId, c.SignedInUser)
|
annotation, respErr := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, cmd.AnnotationId, c.SignedInUser)
|
||||||
if respErr != nil {
|
if respErr != nil {
|
||||||
return respErr
|
return respErr
|
||||||
}
|
}
|
||||||
@ -431,7 +420,7 @@ func (hs *HTTPServer) MassDeleteAnnotations(c *models.ReqContext) response.Respo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = repo.Delete(c.Req.Context(), deleteParams)
|
err = hs.annotationsRepo.Delete(c.Req.Context(), deleteParams)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Failed to delete annotations", err)
|
return response.Error(500, "Failed to delete annotations", err)
|
||||||
@ -454,9 +443,7 @@ func (hs *HTTPServer) GetAnnotationByID(c *models.ReqContext) response.Response
|
|||||||
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
|
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := annotations.GetRepository()
|
annotation, resp := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, annotationID, c.SignedInUser)
|
||||||
|
|
||||||
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
|
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
@ -485,9 +472,7 @@ func (hs *HTTPServer) DeleteAnnotationByID(c *models.ReqContext) response.Respon
|
|||||||
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
|
return response.Error(http.StatusBadRequest, "annotationId is invalid", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := annotations.GetRepository()
|
annotation, resp := findAnnotationByID(c.Req.Context(), hs.annotationsRepo, annotationID, c.SignedInUser)
|
||||||
|
|
||||||
annotation, resp := findAnnotationByID(c.Req.Context(), repo, annotationID, c.SignedInUser)
|
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
@ -496,7 +481,7 @@ func (hs *HTTPServer) DeleteAnnotationByID(c *models.ReqContext) response.Respon
|
|||||||
return dashboardGuardianResponse(err)
|
return dashboardGuardianResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = repo.Delete(c.Req.Context(), &annotations.DeleteParams{
|
err = hs.annotationsRepo.Delete(c.Req.Context(), &annotations.DeleteParams{
|
||||||
OrgId: c.OrgID,
|
OrgId: c.OrgID,
|
||||||
Id: annotationID,
|
Id: annotationID,
|
||||||
})
|
})
|
||||||
@ -563,8 +548,7 @@ func (hs *HTTPServer) GetAnnotationTags(c *models.ReqContext) response.Response
|
|||||||
Limit: c.QueryInt64("limit"),
|
Limit: c.QueryInt64("limit"),
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := annotations.GetRepository()
|
result, err := hs.annotationsRepo.FindTags(c.Req.Context(), query)
|
||||||
result, err := repo.FindTags(c.Req.Context(), query)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Failed to find annotation tags", err)
|
return response.Error(500, "Failed to find annotation tags", err)
|
||||||
}
|
}
|
||||||
@ -575,7 +559,7 @@ func (hs *HTTPServer) GetAnnotationTags(c *models.ReqContext) response.Response
|
|||||||
// AnnotationTypeScopeResolver provides an ScopeAttributeResolver able to
|
// AnnotationTypeScopeResolver provides an ScopeAttributeResolver able to
|
||||||
// resolve annotation types. Scope "annotations:id:<id>" will be translated to "annotations:type:<type>,
|
// resolve annotation types. Scope "annotations:id:<id>" will be translated to "annotations:type:<type>,
|
||||||
// where <type> is the type of annotation with id <id>.
|
// where <type> is the type of annotation with id <id>.
|
||||||
func AnnotationTypeScopeResolver() (string, accesscontrol.ScopeAttributeResolver) {
|
func AnnotationTypeScopeResolver(annotationsRepo annotations.Repository) (string, accesscontrol.ScopeAttributeResolver) {
|
||||||
prefix := accesscontrol.ScopeAnnotationsProvider.GetResourceScope("")
|
prefix := accesscontrol.ScopeAnnotationsProvider.GetResourceScope("")
|
||||||
return prefix, accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, initialScope string) ([]string, error) {
|
return prefix, accesscontrol.ScopeAttributeResolverFunc(func(ctx context.Context, orgID int64, initialScope string) ([]string, error) {
|
||||||
scopeParts := strings.Split(initialScope, ":")
|
scopeParts := strings.Split(initialScope, ":")
|
||||||
@ -601,7 +585,7 @@ func AnnotationTypeScopeResolver() (string, accesscontrol.ScopeAttributeResolver
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
annotation, resp := findAnnotationByID(ctx, annotations.GetRepository(), int64(annotationId), tempUser)
|
annotation, resp := findAnnotationByID(ctx, annotationsRepo, int64(annotationId), tempUser)
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
return nil, errors.New("could not resolve annotation type")
|
return nil, errors.New("could not resolve annotation type")
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/annotations"
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/guardian"
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
@ -73,8 +74,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
|
|||||||
mock := mockstore.NewSQLStoreMock()
|
mock := mockstore.NewSQLStoreMock()
|
||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
|
||||||
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
|
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
|
||||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
sc.handlerFunc = hs.DeleteAnnotationByID
|
sc.handlerFunc = hs.DeleteAnnotationByID
|
||||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
@ -103,8 +102,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
|
|||||||
mock := mockstore.NewSQLStoreMock()
|
mock := mockstore.NewSQLStoreMock()
|
||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
|
||||||
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
|
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
|
||||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
sc.handlerFunc = hs.DeleteAnnotationByID
|
sc.handlerFunc = hs.DeleteAnnotationByID
|
||||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
@ -178,8 +175,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
|
|||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
|
||||||
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
|
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
|
||||||
setUpACL()
|
setUpACL()
|
||||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
sc.handlerFunc = hs.DeleteAnnotationByID
|
sc.handlerFunc = hs.DeleteAnnotationByID
|
||||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
assert.Equal(t, 403, sc.resp.Code)
|
||||||
@ -211,8 +206,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
|
|||||||
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
|
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
|
||||||
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
|
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
|
||||||
setUpACL()
|
setUpACL()
|
||||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
sc.handlerFunc = hs.DeleteAnnotationByID
|
sc.handlerFunc = hs.DeleteAnnotationByID
|
||||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
assert.Equal(t, 200, sc.resp.Code)
|
||||||
@ -286,59 +279,6 @@ func TestAnnotationsAPIEndpoint(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeAnnotationsRepo struct {
|
|
||||||
annotations map[int64]annotations.Item
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFakeAnnotationsRepo() *fakeAnnotationsRepo {
|
|
||||||
return &fakeAnnotationsRepo{
|
|
||||||
annotations: map[int64]annotations.Item{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *fakeAnnotationsRepo) Delete(_ context.Context, params *annotations.DeleteParams) error {
|
|
||||||
if params.Id != 0 {
|
|
||||||
delete(repo.annotations, params.Id)
|
|
||||||
} else {
|
|
||||||
for _, v := range repo.annotations {
|
|
||||||
if params.DashboardId == v.DashboardId && params.PanelId == v.PanelId {
|
|
||||||
delete(repo.annotations, v.Id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (repo *fakeAnnotationsRepo) Save(item *annotations.Item) error {
|
|
||||||
if item.Id == 0 {
|
|
||||||
item.Id = int64(len(repo.annotations) + 1)
|
|
||||||
}
|
|
||||||
repo.annotations[item.Id] = *item
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (repo *fakeAnnotationsRepo) Update(_ context.Context, item *annotations.Item) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (repo *fakeAnnotationsRepo) Find(_ context.Context, query *annotations.ItemQuery) ([]*annotations.ItemDTO, error) {
|
|
||||||
if annotation, has := repo.annotations[query.AnnotationId]; has {
|
|
||||||
return []*annotations.ItemDTO{{Id: annotation.Id, DashboardId: annotation.DashboardId}}, nil
|
|
||||||
}
|
|
||||||
annotations := []*annotations.ItemDTO{{Id: 1, DashboardId: 0}}
|
|
||||||
return annotations, nil
|
|
||||||
}
|
|
||||||
func (repo *fakeAnnotationsRepo) FindTags(_ context.Context, query *annotations.TagsQuery) (annotations.FindTagsResult, error) {
|
|
||||||
result := annotations.FindTagsResult{
|
|
||||||
Tags: []*annotations.TagsDTO{},
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *fakeAnnotationsRepo) LoadItems() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var fakeAnnoRepo *fakeAnnotationsRepo
|
|
||||||
|
|
||||||
func postAnnotationScenario(t *testing.T, desc string, url string, routePattern string, role org.RoleType,
|
func postAnnotationScenario(t *testing.T, desc string, url string, routePattern string, role org.RoleType,
|
||||||
cmd dtos.PostAnnotationsCmd, store sqlstore.Store, dashSvc dashboards.DashboardService, fn scenarioFunc) {
|
cmd dtos.PostAnnotationsCmd, store sqlstore.Store, dashSvc dashboards.DashboardService, fn scenarioFunc) {
|
||||||
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
|
||||||
@ -358,9 +298,6 @@ func postAnnotationScenario(t *testing.T, desc string, url string, routePattern
|
|||||||
return hs.PostAnnotation(c)
|
return hs.PostAnnotation(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
|
|
||||||
sc.m.Post(routePattern, sc.defaultHandler)
|
sc.m.Post(routePattern, sc.defaultHandler)
|
||||||
|
|
||||||
fn(sc)
|
fn(sc)
|
||||||
@ -387,9 +324,6 @@ func putAnnotationScenario(t *testing.T, desc string, url string, routePattern s
|
|||||||
return hs.UpdateAnnotation(c)
|
return hs.UpdateAnnotation(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
|
|
||||||
sc.m.Put(routePattern, sc.defaultHandler)
|
sc.m.Put(routePattern, sc.defaultHandler)
|
||||||
|
|
||||||
fn(sc)
|
fn(sc)
|
||||||
@ -415,9 +349,6 @@ func patchAnnotationScenario(t *testing.T, desc string, url string, routePattern
|
|||||||
return hs.PatchAnnotation(c)
|
return hs.PatchAnnotation(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
|
|
||||||
sc.m.Patch(routePattern, sc.defaultHandler)
|
sc.m.Patch(routePattern, sc.defaultHandler)
|
||||||
|
|
||||||
fn(sc)
|
fn(sc)
|
||||||
@ -443,9 +374,6 @@ func deleteAnnotationsScenario(t *testing.T, desc string, url string, routePatte
|
|||||||
return hs.MassDeleteAnnotations(c)
|
return hs.MassDeleteAnnotations(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
|
|
||||||
sc.m.Post(routePattern, sc.defaultHandler)
|
sc.m.Post(routePattern, sc.defaultHandler)
|
||||||
|
|
||||||
fn(sc)
|
fn(sc)
|
||||||
@ -461,11 +389,8 @@ func TestAPI_Annotations_AccessControl(t *testing.T) {
|
|||||||
dashboardAnnotation := &annotations.Item{Id: 1, DashboardId: 1}
|
dashboardAnnotation := &annotations.Item{Id: 1, DashboardId: 1}
|
||||||
organizationAnnotation := &annotations.Item{Id: 2, DashboardId: 0}
|
organizationAnnotation := &annotations.Item{Id: 2, DashboardId: 0}
|
||||||
|
|
||||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
_ = sc.hs.annotationsRepo.Save(context.Background(), dashboardAnnotation)
|
||||||
_ = fakeAnnoRepo.Save(dashboardAnnotation)
|
_ = sc.hs.annotationsRepo.Save(context.Background(), organizationAnnotation)
|
||||||
_ = fakeAnnoRepo.Save(organizationAnnotation)
|
|
||||||
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
|
|
||||||
postOrganizationCmd := dtos.PostAnnotationsCmd{
|
postOrganizationCmd := dtos.PostAnnotationsCmd{
|
||||||
Time: 1000,
|
Time: 1000,
|
||||||
@ -788,7 +713,7 @@ func TestAPI_Annotations_AccessControl(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
setUpRBACGuardian(t)
|
setUpRBACGuardian(t)
|
||||||
sc.acmock.
|
sc.acmock.
|
||||||
RegisterScopeAttributeResolver(AnnotationTypeScopeResolver())
|
RegisterScopeAttributeResolver(AnnotationTypeScopeResolver(sc.hs.annotationsRepo))
|
||||||
setAccessControlPermissions(sc.acmock, tt.args.permissions, sc.initCtx.OrgID)
|
setAccessControlPermissions(sc.acmock, tt.args.permissions, sc.initCtx.OrgID)
|
||||||
|
|
||||||
r := callAPI(sc.server, tt.args.method, tt.args.url, tt.args.body, t)
|
r := callAPI(sc.server, tt.args.method, tt.args.url, tt.args.body, t)
|
||||||
@ -835,13 +760,11 @@ func TestService_AnnotationTypeScopeResolver(t *testing.T) {
|
|||||||
dashboardAnnotation := annotations.Item{Id: 1, DashboardId: 1}
|
dashboardAnnotation := annotations.Item{Id: 1, DashboardId: 1}
|
||||||
organizationAnnotation := annotations.Item{Id: 2}
|
organizationAnnotation := annotations.Item{Id: 2}
|
||||||
|
|
||||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
|
||||||
_ = fakeAnnoRepo.Save(&dashboardAnnotation)
|
_ = fakeAnnoRepo.Save(context.Background(), &dashboardAnnotation)
|
||||||
_ = fakeAnnoRepo.Save(&organizationAnnotation)
|
_ = fakeAnnoRepo.Save(context.Background(), &organizationAnnotation)
|
||||||
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
prefix, resolver := AnnotationTypeScopeResolver(fakeAnnoRepo)
|
||||||
|
|
||||||
prefix, resolver := AnnotationTypeScopeResolver()
|
|
||||||
require.Equal(t, "annotations:id:", prefix)
|
require.Equal(t, "annotations:id:", prefix)
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
@ -986,11 +909,8 @@ func TestAPI_MassDeleteAnnotations_AccessControl(t *testing.T) {
|
|||||||
dashboardAnnotation := &annotations.Item{Id: 1, DashboardId: 1}
|
dashboardAnnotation := &annotations.Item{Id: 1, DashboardId: 1}
|
||||||
organizationAnnotation := &annotations.Item{Id: 2, DashboardId: 0}
|
organizationAnnotation := &annotations.Item{Id: 2, DashboardId: 0}
|
||||||
|
|
||||||
fakeAnnoRepo = NewFakeAnnotationsRepo()
|
_ = sc.hs.annotationsRepo.Save(context.Background(), dashboardAnnotation)
|
||||||
_ = fakeAnnoRepo.Save(dashboardAnnotation)
|
_ = sc.hs.annotationsRepo.Save(context.Background(), organizationAnnotation)
|
||||||
_ = fakeAnnoRepo.Save(organizationAnnotation)
|
|
||||||
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
|
|
||||||
r := callAPI(sc.server, tt.args.method, tt.args.url, tt.args.body, t)
|
r := callAPI(sc.server, tt.args.method, tt.args.url, tt.args.body, t)
|
||||||
assert.Equalf(t, tt.want, r.Code, "Annotations API(%v)", tt.args.url)
|
assert.Equalf(t, tt.want, r.Code, "Annotations API(%v)", tt.args.url)
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/auth"
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
"github.com/grafana/grafana/pkg/services/contexthandler"
|
"github.com/grafana/grafana/pkg/services/contexthandler"
|
||||||
"github.com/grafana/grafana/pkg/services/contexthandler/authproxy"
|
"github.com/grafana/grafana/pkg/services/contexthandler/authproxy"
|
||||||
@ -337,10 +338,11 @@ func setupSimpleHTTPServer(features *featuremgmt.FeatureManager) *HTTPServer {
|
|||||||
cfg.IsFeatureToggleEnabled = features.IsEnabled
|
cfg.IsFeatureToggleEnabled = features.IsEnabled
|
||||||
|
|
||||||
return &HTTPServer{
|
return &HTTPServer{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
Features: features,
|
Features: features,
|
||||||
License: &licensing.OSSLicensingService{},
|
License: &licensing.OSSLicensingService{},
|
||||||
AccessControl: accesscontrolmock.New().WithDisabled(),
|
AccessControl: accesscontrolmock.New().WithDisabled(),
|
||||||
|
annotationsRepo: annotationstest.NewFakeAnnotationsRepo(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,6 +410,7 @@ func setupHTTPServerWithCfgDb(
|
|||||||
),
|
),
|
||||||
preferenceService: preftest.NewPreferenceServiceFake(),
|
preferenceService: preftest.NewPreferenceServiceFake(),
|
||||||
userService: userMock,
|
userService: userMock,
|
||||||
|
annotationsRepo: annotationstest.NewFakeAnnotationsRepo(),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range options {
|
for _, o := range options {
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards/service"
|
"github.com/grafana/grafana/pkg/services/dashboards/service"
|
||||||
@ -109,7 +110,7 @@ func newTestLive(t *testing.T, store *sqlstore.SQLStore) *live.GrafanaLive {
|
|||||||
nil,
|
nil,
|
||||||
&usagestats.UsageStatsMock{T: t},
|
&usagestats.UsageStatsMock{T: t},
|
||||||
nil,
|
nil,
|
||||||
features, accesscontrolmock.New(), &dashboards.FakeDashboardService{})
|
features, accesscontrolmock.New(), &dashboards.FakeDashboardService{}, annotationstest.NewFakeAnnotationsRepo())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return gLive
|
return gLive
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/plugins/plugincontext"
|
"github.com/grafana/grafana/pkg/plugins/plugincontext"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
"github.com/grafana/grafana/pkg/services/apikey"
|
"github.com/grafana/grafana/pkg/services/apikey"
|
||||||
"github.com/grafana/grafana/pkg/services/cleanup"
|
"github.com/grafana/grafana/pkg/services/cleanup"
|
||||||
"github.com/grafana/grafana/pkg/services/comments"
|
"github.com/grafana/grafana/pkg/services/comments"
|
||||||
@ -189,6 +190,7 @@ type HTTPServer struct {
|
|||||||
loginAttemptService loginAttempt.Service
|
loginAttemptService loginAttempt.Service
|
||||||
orgService org.Service
|
orgService org.Service
|
||||||
accesscontrolService accesscontrol.Service
|
accesscontrolService accesscontrol.Service
|
||||||
|
annotationsRepo annotations.Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServerOptions struct {
|
type ServerOptions struct {
|
||||||
@ -227,7 +229,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
secretsMigrator secrets.Migrator, secretsPluginManager plugins.SecretsPluginManager, secretsService secrets.Service,
|
secretsMigrator secrets.Migrator, secretsPluginManager plugins.SecretsPluginManager, secretsService secrets.Service,
|
||||||
secretsPluginMigrator spm.SecretMigrationProvider, secretsStore secretsKV.SecretsKVStore,
|
secretsPluginMigrator spm.SecretMigrationProvider, secretsStore secretsKV.SecretsKVStore,
|
||||||
publicDashboardsApi *publicdashboardsApi.Api, userService user.Service, tempUserService tempUser.Service, loginAttemptService loginAttempt.Service, orgService org.Service,
|
publicDashboardsApi *publicdashboardsApi.Api, userService user.Service, tempUserService tempUser.Service, loginAttemptService loginAttempt.Service, orgService org.Service,
|
||||||
accesscontrolService accesscontrol.Service, dashboardThumbsService dashboardThumbs.Service,
|
accesscontrolService accesscontrol.Service, dashboardThumbsService dashboardThumbs.Service, annotationRepo annotations.Repository,
|
||||||
) (*HTTPServer, error) {
|
) (*HTTPServer, error) {
|
||||||
web.Env = cfg.Env
|
web.Env = cfg.Env
|
||||||
m := web.New()
|
m := web.New()
|
||||||
@ -323,6 +325,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
loginAttemptService: loginAttemptService,
|
loginAttemptService: loginAttemptService,
|
||||||
orgService: orgService,
|
orgService: orgService,
|
||||||
accesscontrolService: accesscontrolService,
|
accesscontrolService: accesscontrolService,
|
||||||
|
annotationsRepo: annotationRepo,
|
||||||
}
|
}
|
||||||
if hs.Listener != nil {
|
if hs.Listener != nil {
|
||||||
hs.log.Debug("Using provided listener")
|
hs.log.Debug("Using provided listener")
|
||||||
@ -330,7 +333,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
|||||||
hs.registerRoutes()
|
hs.registerRoutes()
|
||||||
|
|
||||||
// Register access control scope resolver for annotations
|
// Register access control scope resolver for annotations
|
||||||
hs.AccessControl.RegisterScopeAttributeResolver(AnnotationTypeScopeResolver())
|
hs.AccessControl.RegisterScopeAttributeResolver(AnnotationTypeScopeResolver(hs.annotationsRepo))
|
||||||
|
|
||||||
if err := hs.declareFixedRoles(); err != nil {
|
if err := hs.declareFixedRoles(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -6,6 +6,8 @@ package server
|
|||||||
import (
|
import (
|
||||||
"github.com/google/wire"
|
"github.com/google/wire"
|
||||||
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationsimpl"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/auth"
|
"github.com/grafana/grafana/pkg/services/auth"
|
||||||
"github.com/grafana/grafana/pkg/services/playlist/playlistimpl"
|
"github.com/grafana/grafana/pkg/services/playlist/playlistimpl"
|
||||||
@ -152,6 +154,8 @@ import (
|
|||||||
var wireBasicSet = wire.NewSet(
|
var wireBasicSet = wire.NewSet(
|
||||||
legacydataservice.ProvideService,
|
legacydataservice.ProvideService,
|
||||||
wire.Bind(new(legacydata.RequestHandler), new(*legacydataservice.Service)),
|
wire.Bind(new(legacydata.RequestHandler), new(*legacydataservice.Service)),
|
||||||
|
annotationsimpl.ProvideService,
|
||||||
|
wire.Bind(new(annotations.Repository), new(*annotationsimpl.RepositoryImpl)),
|
||||||
alerting.ProvideAlertStore,
|
alerting.ProvideAlertStore,
|
||||||
alerting.ProvideAlertEngine,
|
alerting.ProvideAlertEngine,
|
||||||
wire.Bind(new(alerting.UsageStatsQuerier), new(*alerting.AlertEngine)),
|
wire.Bind(new(alerting.UsageStatsQuerier), new(*alerting.AlertEngine)),
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting/metrics"
|
"github.com/grafana/grafana/pkg/services/alerting/metrics"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
"github.com/grafana/grafana/pkg/services/encryption"
|
"github.com/grafana/grafana/pkg/services/encryption"
|
||||||
@ -48,6 +49,7 @@ type AlertEngine struct {
|
|||||||
dashAlertExtractor DashAlertExtractor
|
dashAlertExtractor DashAlertExtractor
|
||||||
dashboardService dashboards.DashboardService
|
dashboardService dashboards.DashboardService
|
||||||
datasourceService datasources.DataSourceService
|
datasourceService datasources.DataSourceService
|
||||||
|
annotationsRepo annotations.Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDisabled returns true if the alerting service is disabled for this instance.
|
// IsDisabled returns true if the alerting service is disabled for this instance.
|
||||||
@ -59,7 +61,7 @@ func (e *AlertEngine) IsDisabled() bool {
|
|||||||
func ProvideAlertEngine(renderer rendering.Service, requestValidator models.PluginRequestValidator,
|
func ProvideAlertEngine(renderer rendering.Service, requestValidator models.PluginRequestValidator,
|
||||||
dataService legacydata.RequestHandler, usageStatsService usagestats.Service, encryptionService encryption.Internal,
|
dataService legacydata.RequestHandler, usageStatsService usagestats.Service, encryptionService encryption.Internal,
|
||||||
notificationService *notifications.NotificationService, tracer tracing.Tracer, store AlertStore, cfg *setting.Cfg,
|
notificationService *notifications.NotificationService, tracer tracing.Tracer, store AlertStore, cfg *setting.Cfg,
|
||||||
dashAlertExtractor DashAlertExtractor, dashboardService dashboards.DashboardService, cacheService *localcache.CacheService, dsService datasources.DataSourceService) *AlertEngine {
|
dashAlertExtractor DashAlertExtractor, dashboardService dashboards.DashboardService, cacheService *localcache.CacheService, dsService datasources.DataSourceService, annotationsRepo annotations.Repository) *AlertEngine {
|
||||||
e := &AlertEngine{
|
e := &AlertEngine{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
RenderService: renderer,
|
RenderService: renderer,
|
||||||
@ -71,6 +73,7 @@ func ProvideAlertEngine(renderer rendering.Service, requestValidator models.Plug
|
|||||||
dashAlertExtractor: dashAlertExtractor,
|
dashAlertExtractor: dashAlertExtractor,
|
||||||
dashboardService: dashboardService,
|
dashboardService: dashboardService,
|
||||||
datasourceService: dsService,
|
datasourceService: dsService,
|
||||||
|
annotationsRepo: annotationsRepo,
|
||||||
}
|
}
|
||||||
e.execQueue = make(chan *Job, 1000)
|
e.execQueue = make(chan *Job, 1000)
|
||||||
e.scheduler = newScheduler()
|
e.scheduler = newScheduler()
|
||||||
@ -193,7 +196,7 @@ func (e *AlertEngine) processJob(attemptID int, attemptChan chan int, cancelChan
|
|||||||
alertCtx, cancelFn := context.WithTimeout(context.Background(), setting.AlertingEvaluationTimeout)
|
alertCtx, cancelFn := context.WithTimeout(context.Background(), setting.AlertingEvaluationTimeout)
|
||||||
cancelChan <- cancelFn
|
cancelChan <- cancelFn
|
||||||
alertCtx, span := e.tracer.Start(alertCtx, "alert execution")
|
alertCtx, span := e.tracer.Start(alertCtx, "alert execution")
|
||||||
evalContext := NewEvalContext(alertCtx, job.Rule, e.RequestValidator, e.AlertStore, e.dashboardService, e.datasourceService)
|
evalContext := NewEvalContext(alertCtx, job.Rule, e.RequestValidator, e.AlertStore, e.dashboardService, e.datasourceService, e.annotationsRepo)
|
||||||
evalContext.Ctx = alertCtx
|
evalContext.Ctx = alertCtx
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
datasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
datasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
||||||
encryptionprovider "github.com/grafana/grafana/pkg/services/encryption/provider"
|
encryptionprovider "github.com/grafana/grafana/pkg/services/encryption/provider"
|
||||||
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
||||||
@ -35,7 +36,8 @@ func TestIntegrationEngineTimeouts(t *testing.T) {
|
|||||||
|
|
||||||
tracer := tracing.InitializeTracerForTest()
|
tracer := tracing.InitializeTracerForTest()
|
||||||
dsMock := &datasources.FakeDataSourceService{}
|
dsMock := &datasources.FakeDataSourceService{}
|
||||||
engine := ProvideAlertEngine(nil, nil, nil, usMock, encService, nil, tracer, nil, setting.NewCfg(), nil, nil, localcache.New(time.Minute, time.Minute), dsMock)
|
annotationsRepo := annotationstest.NewFakeAnnotationsRepo()
|
||||||
|
engine := ProvideAlertEngine(nil, nil, nil, usMock, encService, nil, tracer, nil, setting.NewCfg(), nil, nil, localcache.New(time.Minute, time.Minute), dsMock, annotationsRepo)
|
||||||
setting.AlertingNotificationTimeout = 30 * time.Second
|
setting.AlertingNotificationTimeout = 30 * time.Second
|
||||||
setting.AlertingMaxAttempts = 3
|
setting.AlertingMaxAttempts = 3
|
||||||
engine.resultHandler = &FakeResultHandler{}
|
engine.resultHandler = &FakeResultHandler{}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/infra/usagestats"
|
"github.com/grafana/grafana/pkg/infra/usagestats"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
fd "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
fd "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
||||||
encryptionprovider "github.com/grafana/grafana/pkg/services/encryption/provider"
|
encryptionprovider "github.com/grafana/grafana/pkg/services/encryption/provider"
|
||||||
@ -128,7 +129,7 @@ func TestEngineProcessJob(t *testing.T) {
|
|||||||
dsMock := &fd.FakeDataSourceService{
|
dsMock := &fd.FakeDataSourceService{
|
||||||
DataSources: []*datasources.DataSource{{Id: 1, Type: datasources.DS_PROMETHEUS}},
|
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)
|
engine := ProvideAlertEngine(nil, nil, nil, usMock, encService, nil, tracer, store, setting.NewCfg(), nil, nil, localcache.New(time.Minute, time.Minute), dsMock, annotationstest.NewFakeAnnotationsRepo())
|
||||||
setting.AlertingEvaluationTimeout = 30 * time.Second
|
setting.AlertingEvaluationTimeout = 30 * time.Second
|
||||||
setting.AlertingNotificationTimeout = 30 * time.Second
|
setting.AlertingNotificationTimeout = 30 * time.Second
|
||||||
setting.AlertingMaxAttempts = 3
|
setting.AlertingMaxAttempts = 3
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
@ -42,11 +43,12 @@ type EvalContext struct {
|
|||||||
Store AlertStore
|
Store AlertStore
|
||||||
dashboardService dashboards.DashboardService
|
dashboardService dashboards.DashboardService
|
||||||
DatasourceService datasources.DataSourceService
|
DatasourceService datasources.DataSourceService
|
||||||
|
annotationRepo annotations.Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEvalContext is the EvalContext constructor.
|
// NewEvalContext is the EvalContext constructor.
|
||||||
func NewEvalContext(alertCtx context.Context, rule *Rule, requestValidator models.PluginRequestValidator,
|
func NewEvalContext(alertCtx context.Context, rule *Rule, requestValidator models.PluginRequestValidator,
|
||||||
alertStore AlertStore, dashboardService dashboards.DashboardService, dsService datasources.DataSourceService) *EvalContext {
|
alertStore AlertStore, dashboardService dashboards.DashboardService, dsService datasources.DataSourceService, annotationRepo annotations.Repository) *EvalContext {
|
||||||
return &EvalContext{
|
return &EvalContext{
|
||||||
Ctx: alertCtx,
|
Ctx: alertCtx,
|
||||||
StartTime: time.Now(),
|
StartTime: time.Now(),
|
||||||
@ -60,6 +62,7 @@ func NewEvalContext(alertCtx context.Context, rule *Rule, requestValidator model
|
|||||||
Store: alertStore,
|
Store: alertStore,
|
||||||
dashboardService: dashboardService,
|
dashboardService: dashboardService,
|
||||||
DatasourceService: dsService,
|
DatasourceService: dsService,
|
||||||
|
annotationRepo: annotationRepo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,11 +10,12 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStateIsUpdatedWhenNeeded(t *testing.T) {
|
func TestStateIsUpdatedWhenNeeded(t *testing.T) {
|
||||||
ctx := NewEvalContext(context.Background(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
ctx := NewEvalContext(context.Background(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
t.Run("ok -> alerting", func(t *testing.T) {
|
t.Run("ok -> alerting", func(t *testing.T) {
|
||||||
ctx.PrevAlertState = models.AlertStateOK
|
ctx.PrevAlertState = models.AlertStateOK
|
||||||
@ -199,7 +200,7 @@ func TestGetStateFromEvalContext(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tcs {
|
for _, tc := range tcs {
|
||||||
evalContext := NewEvalContext(context.Background(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
evalContext := NewEvalContext(context.Background(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
tc.applyFn(evalContext)
|
tc.applyFn(evalContext)
|
||||||
newState := evalContext.GetNewState()
|
newState := evalContext.GetNewState()
|
||||||
@ -391,7 +392,7 @@ func TestEvaluateNotificationTemplateFields(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(tt *testing.T) {
|
t.Run(test.name, func(tt *testing.T) {
|
||||||
evalContext := NewEvalContext(context.Background(), &Rule{Name: "Rule name: ${value1}", Message: "Rule message: ${value2}",
|
evalContext := NewEvalContext(context.Background(), &Rule{Name: "Rule name: ${value1}", Message: "Rule message: ${value2}",
|
||||||
Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalContext.EvalMatches = test.evalMatches
|
evalContext.EvalMatches = test.evalMatches
|
||||||
evalContext.AllMatches = test.allMatches
|
evalContext.AllMatches = test.allMatches
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
"github.com/grafana/grafana/pkg/tsdb/legacydata"
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
Conditions: []Condition{&conditionStub{
|
Conditions: []Condition{&conditionStub{
|
||||||
firing: true,
|
firing: true,
|
||||||
}},
|
}},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.Equal(t, true, context.Firing)
|
require.Equal(t, true, context.Firing)
|
||||||
@ -39,7 +40,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
t.Run("Show return triggered with single passing condition2", func(t *testing.T) {
|
t.Run("Show return triggered with single passing condition2", func(t *testing.T) {
|
||||||
context := NewEvalContext(context.Background(), &Rule{
|
context := NewEvalContext(context.Background(), &Rule{
|
||||||
Conditions: []Condition{&conditionStub{firing: true, operator: "and"}},
|
Conditions: []Condition{&conditionStub{firing: true, operator: "and"}},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.Equal(t, true, context.Firing)
|
require.Equal(t, true, context.Firing)
|
||||||
@ -52,7 +53,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
&conditionStub{firing: true, operator: "and", matches: []*EvalMatch{{}, {}}},
|
&conditionStub{firing: true, operator: "and", matches: []*EvalMatch{{}, {}}},
|
||||||
&conditionStub{firing: false, operator: "and"},
|
&conditionStub{firing: false, operator: "and"},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.Equal(t, false, context.Firing)
|
require.Equal(t, false, context.Firing)
|
||||||
@ -65,7 +66,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
&conditionStub{firing: true, operator: "and"},
|
&conditionStub{firing: true, operator: "and"},
|
||||||
&conditionStub{firing: false, operator: "or"},
|
&conditionStub{firing: false, operator: "or"},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.Equal(t, true, context.Firing)
|
require.Equal(t, true, context.Firing)
|
||||||
@ -78,7 +79,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
&conditionStub{firing: true, operator: "and"},
|
&conditionStub{firing: true, operator: "and"},
|
||||||
&conditionStub{firing: false, operator: "and"},
|
&conditionStub{firing: false, operator: "and"},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.Equal(t, false, context.Firing)
|
require.Equal(t, false, context.Firing)
|
||||||
@ -92,7 +93,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
&conditionStub{firing: true, operator: "and"},
|
&conditionStub{firing: true, operator: "and"},
|
||||||
&conditionStub{firing: false, operator: "or"},
|
&conditionStub{firing: false, operator: "or"},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.Equal(t, true, context.Firing)
|
require.Equal(t, true, context.Firing)
|
||||||
@ -106,7 +107,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
&conditionStub{firing: false, operator: "and"},
|
&conditionStub{firing: false, operator: "and"},
|
||||||
&conditionStub{firing: false, operator: "or"},
|
&conditionStub{firing: false, operator: "or"},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.Equal(t, false, context.Firing)
|
require.Equal(t, false, context.Firing)
|
||||||
@ -120,7 +121,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
&conditionStub{firing: false, operator: "and"},
|
&conditionStub{firing: false, operator: "and"},
|
||||||
&conditionStub{firing: true, operator: "and"},
|
&conditionStub{firing: true, operator: "and"},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.Equal(t, false, context.Firing)
|
require.Equal(t, false, context.Firing)
|
||||||
@ -134,7 +135,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
&conditionStub{firing: false, operator: "or"},
|
&conditionStub{firing: false, operator: "or"},
|
||||||
&conditionStub{firing: true, operator: "or"},
|
&conditionStub{firing: true, operator: "or"},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.Equal(t, true, context.Firing)
|
require.Equal(t, true, context.Firing)
|
||||||
@ -148,7 +149,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
&conditionStub{firing: false, operator: "or"},
|
&conditionStub{firing: false, operator: "or"},
|
||||||
&conditionStub{firing: false, operator: "or"},
|
&conditionStub{firing: false, operator: "or"},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.Equal(t, false, context.Firing)
|
require.Equal(t, false, context.Firing)
|
||||||
@ -163,7 +164,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
&conditionStub{operator: "or", noData: false},
|
&conditionStub{operator: "or", noData: false},
|
||||||
&conditionStub{operator: "or", noData: false},
|
&conditionStub{operator: "or", noData: false},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.False(t, context.NoDataFound)
|
require.False(t, context.NoDataFound)
|
||||||
@ -174,7 +175,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
Conditions: []Condition{
|
Conditions: []Condition{
|
||||||
&conditionStub{operator: "and", noData: true},
|
&conditionStub{operator: "and", noData: true},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.Equal(t, false, context.Firing)
|
require.Equal(t, false, context.Firing)
|
||||||
@ -187,7 +188,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
&conditionStub{operator: "and", noData: true},
|
&conditionStub{operator: "and", noData: true},
|
||||||
&conditionStub{operator: "and", noData: false},
|
&conditionStub{operator: "and", noData: false},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.True(t, context.NoDataFound)
|
require.True(t, context.NoDataFound)
|
||||||
@ -199,7 +200,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
|
|||||||
&conditionStub{operator: "or", noData: true},
|
&conditionStub{operator: "or", noData: true},
|
||||||
&conditionStub{operator: "or", noData: false},
|
&conditionStub{operator: "or", noData: false},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
handler.Eval(context)
|
handler.Eval(context)
|
||||||
require.True(t, context.NoDataFound)
|
require.True(t, context.NoDataFound)
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/imguploader"
|
"github.com/grafana/grafana/pkg/components/imguploader"
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
"github.com/grafana/grafana/pkg/services/notifications"
|
||||||
"github.com/grafana/grafana/pkg/services/rendering"
|
"github.com/grafana/grafana/pkg/services/rendering"
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
@ -20,18 +21,18 @@ import (
|
|||||||
func TestNotificationService(t *testing.T) {
|
func TestNotificationService(t *testing.T) {
|
||||||
testRule := &Rule{Name: "Test", Message: "Something is bad"}
|
testRule := &Rule{Name: "Test", Message: "Something is bad"}
|
||||||
store := &AlertStoreMock{}
|
store := &AlertStoreMock{}
|
||||||
evalCtx := NewEvalContext(context.Background(), testRule, &validations.OSSPluginRequestValidator{}, store, nil, nil)
|
evalCtx := NewEvalContext(context.Background(), testRule, &validations.OSSPluginRequestValidator{}, store, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
testRuleTemplated := &Rule{Name: "Test latency ${quantile}", Message: "Something is bad on instance ${instance}"}
|
testRuleTemplated := &Rule{Name: "Test latency ${quantile}", Message: "Something is bad on instance ${instance}"}
|
||||||
|
|
||||||
evalCtxWithMatch := NewEvalContext(context.Background(), testRuleTemplated, &validations.OSSPluginRequestValidator{}, store, nil, nil)
|
evalCtxWithMatch := NewEvalContext(context.Background(), testRuleTemplated, &validations.OSSPluginRequestValidator{}, store, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalCtxWithMatch.EvalMatches = []*EvalMatch{{
|
evalCtxWithMatch.EvalMatches = []*EvalMatch{{
|
||||||
Tags: map[string]string{
|
Tags: map[string]string{
|
||||||
"instance": "localhost:3000",
|
"instance": "localhost:3000",
|
||||||
"quantile": "0.99",
|
"quantile": "0.99",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
evalCtxWithoutMatch := NewEvalContext(context.Background(), testRuleTemplated, &validations.OSSPluginRequestValidator{}, store, nil, nil)
|
evalCtxWithoutMatch := NewEvalContext(context.Background(), testRuleTemplated, &validations.OSSPluginRequestValidator{}, store, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
notificationServiceScenario(t, "Given alert rule with upload image enabled should render and upload image and send notification",
|
notificationServiceScenario(t, "Given alert rule with upload image enabled should render and upload image and send notification",
|
||||||
evalCtx, true, func(sc *scenarioContext) {
|
evalCtx, true, func(sc *scenarioContext) {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
|
|
||||||
@ -68,7 +69,7 @@ func TestWhenAlertManagerShouldNotify(t *testing.T) {
|
|||||||
am := &AlertmanagerNotifier{log: log.New("test.logger")}
|
am := &AlertmanagerNotifier{log: log.New("test.logger")}
|
||||||
evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{
|
evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{
|
||||||
State: tc.prevState,
|
State: tc.prevState,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
evalContext.Rule.State = tc.newState
|
evalContext.Rule.State = tc.newState
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -170,7 +171,7 @@ func TestShouldSendAlertNotification(t *testing.T) {
|
|||||||
for _, tc := range tcs {
|
for _, tc := range tcs {
|
||||||
evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{
|
evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{
|
||||||
State: tc.prevState,
|
State: tc.prevState,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
if tc.state == nil {
|
if tc.state == nil {
|
||||||
tc.state = &models.AlertNotificationState{}
|
tc.state = &models.AlertNotificationState{}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ func TestDingDingNotifier(t *testing.T) {
|
|||||||
&alerting.Rule{
|
&alerting.Rule{
|
||||||
State: models.AlertStateAlerting,
|
State: models.AlertStateAlerting,
|
||||||
Message: `{host="localhost"}`,
|
Message: `{host="localhost"}`,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
_, err = notifier.genBody(evalContext, "")
|
_, err = notifier.genBody(evalContext, "")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
})
|
})
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
"github.com/grafana/grafana/pkg/services/notifications"
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
@ -105,7 +106,7 @@ func TestOpsGenieNotifier(t *testing.T) {
|
|||||||
Message: "someMessage",
|
Message: "someMessage",
|
||||||
State: models.AlertStateAlerting,
|
State: models.AlertStateAlerting,
|
||||||
AlertRuleTags: tagPairs,
|
AlertRuleTags: tagPairs,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalContext.IsTestRun = true
|
evalContext.IsTestRun = true
|
||||||
|
|
||||||
tags := make([]string, 0)
|
tags := make([]string, 0)
|
||||||
@ -154,7 +155,7 @@ func TestOpsGenieNotifier(t *testing.T) {
|
|||||||
Message: "someMessage",
|
Message: "someMessage",
|
||||||
State: models.AlertStateAlerting,
|
State: models.AlertStateAlerting,
|
||||||
AlertRuleTags: tagPairs,
|
AlertRuleTags: tagPairs,
|
||||||
}, nil, nil, nil, nil)
|
}, nil, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalContext.IsTestRun = true
|
evalContext.IsTestRun = true
|
||||||
|
|
||||||
tags := make([]string, 0)
|
tags := make([]string, 0)
|
||||||
@ -203,7 +204,7 @@ func TestOpsGenieNotifier(t *testing.T) {
|
|||||||
Message: "someMessage",
|
Message: "someMessage",
|
||||||
State: models.AlertStateAlerting,
|
State: models.AlertStateAlerting,
|
||||||
AlertRuleTags: tagPairs,
|
AlertRuleTags: tagPairs,
|
||||||
}, nil, nil, nil, nil)
|
}, nil, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalContext.IsTestRun = true
|
evalContext.IsTestRun = true
|
||||||
|
|
||||||
tags := make([]string, 0)
|
tags := make([]string, 0)
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
|
|
||||||
@ -142,7 +143,7 @@ func TestPagerdutyNotifier(t *testing.T) {
|
|||||||
Name: "someRule",
|
Name: "someRule",
|
||||||
Message: "someMessage",
|
Message: "someMessage",
|
||||||
State: models.AlertStateAlerting,
|
State: models.AlertStateAlerting,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalContext.IsTestRun = true
|
evalContext.IsTestRun = true
|
||||||
|
|
||||||
payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext)
|
payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext)
|
||||||
@ -198,7 +199,7 @@ func TestPagerdutyNotifier(t *testing.T) {
|
|||||||
ID: 0,
|
ID: 0,
|
||||||
Name: "someRule",
|
Name: "someRule",
|
||||||
State: models.AlertStateAlerting,
|
State: models.AlertStateAlerting,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalContext.IsTestRun = true
|
evalContext.IsTestRun = true
|
||||||
|
|
||||||
payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext)
|
payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext)
|
||||||
@ -256,7 +257,7 @@ func TestPagerdutyNotifier(t *testing.T) {
|
|||||||
Name: "someRule",
|
Name: "someRule",
|
||||||
Message: "someMessage",
|
Message: "someMessage",
|
||||||
State: models.AlertStateAlerting,
|
State: models.AlertStateAlerting,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalContext.IsTestRun = true
|
evalContext.IsTestRun = true
|
||||||
evalContext.EvalMatches = []*alerting.EvalMatch{
|
evalContext.EvalMatches = []*alerting.EvalMatch{
|
||||||
{
|
{
|
||||||
@ -335,7 +336,7 @@ func TestPagerdutyNotifier(t *testing.T) {
|
|||||||
{Key: "severity", Value: "warning"},
|
{Key: "severity", Value: "warning"},
|
||||||
{Key: "dedup_key", Value: "key-" + strings.Repeat("x", 260)},
|
{Key: "dedup_key", Value: "key-" + strings.Repeat("x", 260)},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png"
|
evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png"
|
||||||
evalContext.IsTestRun = true
|
evalContext.IsTestRun = true
|
||||||
|
|
||||||
@ -414,7 +415,7 @@ func TestPagerdutyNotifier(t *testing.T) {
|
|||||||
{Key: "component", Value: "aComponent"},
|
{Key: "component", Value: "aComponent"},
|
||||||
{Key: "severity", Value: "info"},
|
{Key: "severity", Value: "info"},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png"
|
evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png"
|
||||||
evalContext.IsTestRun = true
|
evalContext.IsTestRun = true
|
||||||
|
|
||||||
@ -493,7 +494,7 @@ func TestPagerdutyNotifier(t *testing.T) {
|
|||||||
{Key: "component", Value: "aComponent"},
|
{Key: "component", Value: "aComponent"},
|
||||||
{Key: "severity", Value: "llama"},
|
{Key: "severity", Value: "llama"},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png"
|
evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png"
|
||||||
evalContext.IsTestRun = true
|
evalContext.IsTestRun = true
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
|
|
||||||
@ -76,7 +77,7 @@ func TestGenPushoverBody(t *testing.T) {
|
|||||||
evalContext := alerting.NewEvalContext(context.Background(),
|
evalContext := alerting.NewEvalContext(context.Background(),
|
||||||
&alerting.Rule{
|
&alerting.Rule{
|
||||||
State: models.AlertStateAlerting,
|
State: models.AlertStateAlerting,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
_, pushoverBody, err := notifier.genPushoverBody(evalContext, "", "")
|
_, pushoverBody, err := notifier.genPushoverBody(evalContext, "", "")
|
||||||
|
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@ -87,7 +88,7 @@ func TestGenPushoverBody(t *testing.T) {
|
|||||||
evalContext := alerting.NewEvalContext(context.Background(),
|
evalContext := alerting.NewEvalContext(context.Background(),
|
||||||
&alerting.Rule{
|
&alerting.Rule{
|
||||||
State: models.AlertStateOK,
|
State: models.AlertStateOK,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
_, pushoverBody, err := notifier.genPushoverBody(evalContext, "", "")
|
_, pushoverBody, err := notifier.genPushoverBody(evalContext, "", "")
|
||||||
|
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
|
|
||||||
@ -61,7 +62,7 @@ func TestTelegramNotifier(t *testing.T) {
|
|||||||
Name: "This is an alarm",
|
Name: "This is an alarm",
|
||||||
Message: "Some kind of message.",
|
Message: "Some kind of message.",
|
||||||
State: models.AlertStateOK,
|
State: models.AlertStateOK,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
caption := generateImageCaption(evalContext, "http://grafa.url/abcdef", "")
|
caption := generateImageCaption(evalContext, "http://grafa.url/abcdef", "")
|
||||||
require.LessOrEqual(t, len(caption), 1024)
|
require.LessOrEqual(t, len(caption), 1024)
|
||||||
@ -77,7 +78,7 @@ func TestTelegramNotifier(t *testing.T) {
|
|||||||
Name: "This is an alarm",
|
Name: "This is an alarm",
|
||||||
Message: "Some kind of message.",
|
Message: "Some kind of message.",
|
||||||
State: models.AlertStateOK,
|
State: models.AlertStateOK,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
caption := generateImageCaption(evalContext,
|
caption := generateImageCaption(evalContext,
|
||||||
"http://grafa.url/abcdefaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
"http://grafa.url/abcdefaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||||
@ -95,7 +96,7 @@ func TestTelegramNotifier(t *testing.T) {
|
|||||||
Name: "This is an alarm",
|
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.",
|
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,
|
State: models.AlertStateOK,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
caption := generateImageCaption(evalContext,
|
caption := generateImageCaption(evalContext,
|
||||||
"http://grafa.url/foo",
|
"http://grafa.url/foo",
|
||||||
@ -112,7 +113,7 @@ func TestTelegramNotifier(t *testing.T) {
|
|||||||
Name: "This is an alarm",
|
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",
|
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,
|
State: models.AlertStateOK,
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
|
|
||||||
caption := generateImageCaption(evalContext,
|
caption := generateImageCaption(evalContext,
|
||||||
"http://grafa.url/foo",
|
"http://grafa.url/foo",
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
|
||||||
"github.com/grafana/grafana/pkg/services/validations"
|
"github.com/grafana/grafana/pkg/services/validations"
|
||||||
|
|
||||||
@ -93,7 +94,7 @@ func TestVictoropsNotifier(t *testing.T) {
|
|||||||
{Key: "keyOnly"},
|
{Key: "keyOnly"},
|
||||||
{Key: "severity", Value: "warning"},
|
{Key: "severity", Value: "warning"},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalContext.IsTestRun = true
|
evalContext.IsTestRun = true
|
||||||
|
|
||||||
payload, err := victoropsNotifier.buildEventPayload(evalContext)
|
payload, err := victoropsNotifier.buildEventPayload(evalContext)
|
||||||
@ -141,7 +142,7 @@ func TestVictoropsNotifier(t *testing.T) {
|
|||||||
{Key: "keyOnly"},
|
{Key: "keyOnly"},
|
||||||
{Key: "severity", Value: "warning"},
|
{Key: "severity", Value: "warning"},
|
||||||
},
|
},
|
||||||
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
|
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
evalContext.IsTestRun = true
|
evalContext.IsTestRun = true
|
||||||
|
|
||||||
payload, err := victoropsNotifier.buildEventPayload(evalContext)
|
payload, err := victoropsNotifier.buildEventPayload(evalContext)
|
||||||
|
@ -95,8 +95,7 @@ func (handler *defaultResultHandler) handle(evalContext *EvalContext) error {
|
|||||||
Data: annotationData,
|
Data: annotationData,
|
||||||
}
|
}
|
||||||
|
|
||||||
annotationRepo := annotations.GetRepository()
|
if err := evalContext.annotationRepo.Save(evalContext.Ctx, &item); err != nil {
|
||||||
if err := annotationRepo.Save(&item); err != nil {
|
|
||||||
handler.log.Error("Failed to save annotation for new alert state", "error", err)
|
handler.log.Error("Failed to save annotation for new alert state", "error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NotificationTestCommand initiates an test
|
// NotificationTestCommand initiates an test
|
||||||
@ -57,7 +58,7 @@ func createTestEvalContext(cmd *NotificationTestCommand) *EvalContext {
|
|||||||
ID: rand.Int63(),
|
ID: rand.Int63(),
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := NewEvalContext(context.Background(), testRule, fakeRequestValidator{}, nil, nil, nil)
|
ctx := NewEvalContext(context.Background(), testRule, fakeRequestValidator{}, nil, nil, nil, annotationstest.NewFakeAnnotationsRepo())
|
||||||
if cmd.Settings.Get("uploadImage").MustBool(true) {
|
if cmd.Settings.Get("uploadImage").MustBool(true) {
|
||||||
ctx.ImagePublicURL = "https://grafana.com/assets/img/blog/mixed_styles.png"
|
ctx.ImagePublicURL = "https://grafana.com/assets/img/blog/mixed_styles.png"
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ func (e *AlertEngine) AlertTest(orgID int64, dashboard *simplejson.Json, panelID
|
|||||||
|
|
||||||
handler := NewEvalHandler(e.DataService)
|
handler := NewEvalHandler(e.DataService)
|
||||||
|
|
||||||
context := NewEvalContext(context.Background(), rule, fakeRequestValidator{}, e.AlertStore, nil, e.datasourceService)
|
context := NewEvalContext(context.Background(), rule, fakeRequestValidator{}, e.AlertStore, nil, e.datasourceService, annotationstest.NewFakeAnnotationsRepo())
|
||||||
context.IsTestRun = true
|
context.IsTestRun = true
|
||||||
context.IsDebug = true
|
context.IsDebug = true
|
||||||
|
|
||||||
|
@ -4,8 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -14,7 +12,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Repository interface {
|
type Repository interface {
|
||||||
Save(item *Item) error
|
Save(ctx context.Context, item *Item) error
|
||||||
Update(ctx context.Context, item *Item) error
|
Update(ctx context.Context, item *Item) error
|
||||||
Find(ctx context.Context, query *ItemQuery) ([]*ItemDTO, error)
|
Find(ctx context.Context, query *ItemQuery) ([]*ItemDTO, error)
|
||||||
Delete(ctx context.Context, params *DeleteParams) error
|
Delete(ctx context.Context, params *DeleteParams) error
|
||||||
@ -26,63 +24,7 @@ type AnnotationCleaner interface {
|
|||||||
CleanAnnotations(ctx context.Context, cfg *setting.Cfg) (int64, int64, error)
|
CleanAnnotations(ctx context.Context, cfg *setting.Cfg) (int64, int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ItemQuery struct {
|
// var repositoryInstance Repository
|
||||||
OrgId int64 `json:"orgId"`
|
|
||||||
From int64 `json:"from"`
|
|
||||||
To int64 `json:"to"`
|
|
||||||
UserId int64 `json:"userId"`
|
|
||||||
AlertId int64 `json:"alertId"`
|
|
||||||
DashboardId int64 `json:"dashboardId"`
|
|
||||||
DashboardUid string `json:"dashboardUID"`
|
|
||||||
PanelId int64 `json:"panelId"`
|
|
||||||
AnnotationId int64 `json:"annotationId"`
|
|
||||||
Tags []string `json:"tags"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
MatchAny bool `json:"matchAny"`
|
|
||||||
SignedInUser *user.SignedInUser
|
|
||||||
|
|
||||||
Limit int64 `json:"limit"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TagsQuery is the query for a tags search.
|
|
||||||
type TagsQuery struct {
|
|
||||||
OrgID int64 `json:"orgId"`
|
|
||||||
Tag string `json:"tag"`
|
|
||||||
|
|
||||||
Limit int64 `json:"limit"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tag is the DB result of a tags search.
|
|
||||||
type Tag struct {
|
|
||||||
Key string
|
|
||||||
Value string
|
|
||||||
Count int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// TagsDTO is the frontend DTO for Tag.
|
|
||||||
type TagsDTO struct {
|
|
||||||
Tag string `json:"tag"`
|
|
||||||
Count int64 `json:"count"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindTagsResult is the result of a tags search.
|
|
||||||
type FindTagsResult struct {
|
|
||||||
Tags []*TagsDTO `json:"tags"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAnnotationTagsResponse is a response struct for FindTagsResult.
|
|
||||||
type GetAnnotationTagsResponse struct {
|
|
||||||
Result FindTagsResult `json:"result"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeleteParams struct {
|
|
||||||
OrgId int64
|
|
||||||
Id int64
|
|
||||||
DashboardId int64
|
|
||||||
PanelId int64
|
|
||||||
}
|
|
||||||
|
|
||||||
var repositoryInstance Repository
|
|
||||||
var cleanerInstance AnnotationCleaner
|
var cleanerInstance AnnotationCleaner
|
||||||
|
|
||||||
func GetAnnotationCleaner() AnnotationCleaner {
|
func GetAnnotationCleaner() AnnotationCleaner {
|
||||||
@ -92,84 +34,3 @@ func GetAnnotationCleaner() AnnotationCleaner {
|
|||||||
func SetAnnotationCleaner(rep AnnotationCleaner) {
|
func SetAnnotationCleaner(rep AnnotationCleaner) {
|
||||||
cleanerInstance = rep
|
cleanerInstance = rep
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetRepository() Repository {
|
|
||||||
return repositoryInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetRepository(rep Repository) {
|
|
||||||
repositoryInstance = rep
|
|
||||||
}
|
|
||||||
|
|
||||||
type Item struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
OrgId int64 `json:"orgId"`
|
|
||||||
UserId int64 `json:"userId"`
|
|
||||||
DashboardId int64 `json:"dashboardId"`
|
|
||||||
PanelId int64 `json:"panelId"`
|
|
||||||
Text string `json:"text"`
|
|
||||||
AlertId int64 `json:"alertId"`
|
|
||||||
PrevState string `json:"prevState"`
|
|
||||||
NewState string `json:"newState"`
|
|
||||||
Epoch int64 `json:"epoch"`
|
|
||||||
EpochEnd int64 `json:"epochEnd"`
|
|
||||||
Created int64 `json:"created"`
|
|
||||||
Updated int64 `json:"updated"`
|
|
||||||
Tags []string `json:"tags"`
|
|
||||||
Data *simplejson.Json `json:"data"`
|
|
||||||
|
|
||||||
// needed until we remove it from db
|
|
||||||
Type string
|
|
||||||
Title string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i Item) TableName() string {
|
|
||||||
return "annotation"
|
|
||||||
}
|
|
||||||
|
|
||||||
type ItemDTO struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
AlertId int64 `json:"alertId"`
|
|
||||||
AlertName string `json:"alertName"`
|
|
||||||
DashboardId int64 `json:"dashboardId"`
|
|
||||||
DashboardUID *string `json:"dashboardUID"`
|
|
||||||
PanelId int64 `json:"panelId"`
|
|
||||||
UserId int64 `json:"userId"`
|
|
||||||
NewState string `json:"newState"`
|
|
||||||
PrevState string `json:"prevState"`
|
|
||||||
Created int64 `json:"created"`
|
|
||||||
Updated int64 `json:"updated"`
|
|
||||||
Time int64 `json:"time"`
|
|
||||||
TimeEnd int64 `json:"timeEnd"`
|
|
||||||
Text string `json:"text"`
|
|
||||||
Tags []string `json:"tags"`
|
|
||||||
Login string `json:"login"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
AvatarUrl string `json:"avatarUrl"`
|
|
||||||
Data *simplejson.Json `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type annotationType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
Organization annotationType = iota
|
|
||||||
Dashboard
|
|
||||||
)
|
|
||||||
|
|
||||||
func (a annotationType) String() string {
|
|
||||||
switch a {
|
|
||||||
case Organization:
|
|
||||||
return "organization"
|
|
||||||
case Dashboard:
|
|
||||||
return "dashboard"
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (annotation *ItemDTO) GetType() annotationType {
|
|
||||||
if annotation.DashboardId != 0 {
|
|
||||||
return Dashboard
|
|
||||||
}
|
|
||||||
return Organization
|
|
||||||
}
|
|
||||||
|
44
pkg/services/annotations/annotationsimpl/annotations.go
Normal file
44
pkg/services/annotations/annotationsimpl/annotations.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package annotationsimpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RepositoryImpl struct {
|
||||||
|
store store
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideService(db db.DB, cfg *setting.Cfg) *RepositoryImpl {
|
||||||
|
return &RepositoryImpl{
|
||||||
|
store: &SQLAnnotationRepo{
|
||||||
|
cfg: cfg,
|
||||||
|
db: db,
|
||||||
|
log: log.New("annotations"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RepositoryImpl) Save(ctx context.Context, item *annotations.Item) error {
|
||||||
|
return r.store.Add(ctx, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RepositoryImpl) Update(ctx context.Context, item *annotations.Item) error {
|
||||||
|
return r.store.Update(ctx, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RepositoryImpl) Find(ctx context.Context, query *annotations.ItemQuery) ([]*annotations.ItemDTO, error) {
|
||||||
|
return r.store.Get(ctx, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RepositoryImpl) Delete(ctx context.Context, params *annotations.DeleteParams) error {
|
||||||
|
return r.store.Delete(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RepositoryImpl) FindTags(ctx context.Context, query *annotations.TagsQuery) (annotations.FindTagsResult, error) {
|
||||||
|
return r.store.GetTags(ctx, query)
|
||||||
|
}
|
15
pkg/services/annotations/annotationsimpl/store.go
Normal file
15
pkg/services/annotations/annotationsimpl/store.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package annotationsimpl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
|
)
|
||||||
|
|
||||||
|
type store interface {
|
||||||
|
Add(ctx context.Context, item *annotations.Item) error
|
||||||
|
Update(ctx context.Context, item *annotations.Item) error
|
||||||
|
Get(ctx context.Context, query *annotations.ItemQuery) ([]*annotations.ItemDTO, error)
|
||||||
|
Delete(ctx context.Context, params *annotations.DeleteParams) error
|
||||||
|
GetTags(ctx context.Context, query *annotations.TagsQuery) (annotations.FindTagsResult, error)
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package sqlstore
|
package annotationsimpl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -8,14 +8,21 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/annotations"
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
|
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var timeNow = time.Now
|
||||||
|
|
||||||
// Update the item so that EpochEnd >= Epoch
|
// Update the item so that EpochEnd >= Epoch
|
||||||
func validateTimeRange(item *annotations.Item) error {
|
func validateTimeRange(item *annotations.Item) error {
|
||||||
if item.EpochEnd == 0 {
|
if item.EpochEnd == 0 {
|
||||||
@ -34,15 +41,13 @@ func validateTimeRange(item *annotations.Item) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SQLAnnotationRepo struct {
|
type SQLAnnotationRepo struct {
|
||||||
sql *SQLStore
|
cfg *setting.Cfg
|
||||||
|
db db.DB
|
||||||
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSQLAnnotationRepo(sql *SQLStore) SQLAnnotationRepo {
|
func (r *SQLAnnotationRepo) Add(ctx context.Context, item *annotations.Item) error {
|
||||||
return SQLAnnotationRepo{sql: sql}
|
return r.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||||
}
|
|
||||||
|
|
||||||
func (r *SQLAnnotationRepo) Save(item *annotations.Item) error {
|
|
||||||
return r.sql.WithTransactionalDbSession(context.Background(), func(sess *DBSession) error {
|
|
||||||
tags := models.ParseTagPairs(item.Tags)
|
tags := models.ParseTagPairs(item.Tags)
|
||||||
item.Tags = models.JoinTagPairs(tags)
|
item.Tags = models.JoinTagPairs(tags)
|
||||||
item.Created = timeNow().UnixNano() / int64(time.Millisecond)
|
item.Created = timeNow().UnixNano() / int64(time.Millisecond)
|
||||||
@ -59,7 +64,7 @@ func (r *SQLAnnotationRepo) Save(item *annotations.Item) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if item.Tags != nil {
|
if item.Tags != nil {
|
||||||
tags, err := EnsureTagsExist(sess, tags)
|
tags, err := sqlstore.EnsureTagsExist(sess, tags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -75,7 +80,7 @@ func (r *SQLAnnotationRepo) Save(item *annotations.Item) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *SQLAnnotationRepo) Update(ctx context.Context, item *annotations.Item) error {
|
func (r *SQLAnnotationRepo) Update(ctx context.Context, item *annotations.Item) error {
|
||||||
return r.sql.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
return r.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||||
var (
|
var (
|
||||||
isExist bool
|
isExist bool
|
||||||
err error
|
err error
|
||||||
@ -106,7 +111,7 @@ func (r *SQLAnnotationRepo) Update(ctx context.Context, item *annotations.Item)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if item.Tags != nil {
|
if item.Tags != nil {
|
||||||
tags, err := EnsureTagsExist(sess, models.ParseTagPairs(item.Tags))
|
tags, err := sqlstore.EnsureTagsExist(sess, models.ParseTagPairs(item.Tags))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -127,11 +132,11 @@ func (r *SQLAnnotationRepo) Update(ctx context.Context, item *annotations.Item)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *SQLAnnotationRepo) Find(ctx context.Context, query *annotations.ItemQuery) ([]*annotations.ItemDTO, error) {
|
func (r *SQLAnnotationRepo) Get(ctx context.Context, query *annotations.ItemQuery) ([]*annotations.ItemDTO, error) {
|
||||||
var sql bytes.Buffer
|
var sql bytes.Buffer
|
||||||
params := make([]interface{}, 0)
|
params := make([]interface{}, 0)
|
||||||
items := make([]*annotations.ItemDTO, 0)
|
items := make([]*annotations.ItemDTO, 0)
|
||||||
err := r.sql.WithDbSession(ctx, func(sess *DBSession) error {
|
err := r.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||||
sql.WriteString(`
|
sql.WriteString(`
|
||||||
SELECT
|
SELECT
|
||||||
annotation.id,
|
annotation.id,
|
||||||
@ -151,7 +156,7 @@ func (r *SQLAnnotationRepo) Find(ctx context.Context, query *annotations.ItemQue
|
|||||||
usr.login,
|
usr.login,
|
||||||
alert.name as alert_name
|
alert.name as alert_name
|
||||||
FROM annotation
|
FROM annotation
|
||||||
LEFT OUTER JOIN ` + dialect.Quote("user") + ` as usr on usr.id = annotation.user_id
|
LEFT OUTER JOIN ` + r.db.GetDialect().Quote("user") + ` as usr on usr.id = annotation.user_id
|
||||||
LEFT OUTER JOIN alert on alert.id = annotation.alert_id
|
LEFT OUTER JOIN alert on alert.id = annotation.alert_id
|
||||||
INNER JOIN (
|
INNER JOIN (
|
||||||
SELECT a.id from annotation a
|
SELECT a.id from annotation a
|
||||||
@ -203,10 +208,10 @@ func (r *SQLAnnotationRepo) Find(ctx context.Context, query *annotations.ItemQue
|
|||||||
tags := models.ParseTagPairs(query.Tags)
|
tags := models.ParseTagPairs(query.Tags)
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
if tag.Value == "" {
|
if tag.Value == "" {
|
||||||
keyValueFilters = append(keyValueFilters, "(tag."+dialect.Quote("key")+" = ?)")
|
keyValueFilters = append(keyValueFilters, "(tag."+r.db.GetDialect().Quote("key")+" = ?)")
|
||||||
params = append(params, tag.Key)
|
params = append(params, tag.Key)
|
||||||
} else {
|
} else {
|
||||||
keyValueFilters = append(keyValueFilters, "(tag."+dialect.Quote("key")+" = ? AND tag."+dialect.Quote("value")+" = ?)")
|
keyValueFilters = append(keyValueFilters, "(tag."+r.db.GetDialect().Quote("key")+" = ? AND tag."+r.db.GetDialect().Quote("value")+" = ?)")
|
||||||
params = append(params, tag.Key, tag.Value)
|
params = append(params, tag.Key, tag.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,7 +234,7 @@ func (r *SQLAnnotationRepo) Find(ctx context.Context, query *annotations.ItemQue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ac.IsDisabled(r.sql.Cfg) {
|
if !ac.IsDisabled(r.cfg) {
|
||||||
acFilter, acArgs, err := getAccessControlFilter(query.SignedInUser)
|
acFilter, acArgs, err := getAccessControlFilter(query.SignedInUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -243,8 +248,9 @@ func (r *SQLAnnotationRepo) Find(ctx context.Context, query *annotations.ItemQue
|
|||||||
}
|
}
|
||||||
|
|
||||||
// order of ORDER BY arguments match the order of a sql index for performance
|
// order of ORDER BY arguments match the order of a sql index for performance
|
||||||
sql.WriteString(" ORDER BY a.org_id, a.epoch_end DESC, a.epoch DESC" + dialect.Limit(query.Limit) + " ) dt on dt.id = annotation.id")
|
sql.WriteString(" ORDER BY a.org_id, a.epoch_end DESC, a.epoch DESC" + r.db.GetDialect().Limit(query.Limit) + " ) dt on dt.id = annotation.id")
|
||||||
|
|
||||||
|
spew.Dump(">>>> query: ", sql.String())
|
||||||
if err := sess.SQL(sql.String(), params...).Find(&items); err != nil {
|
if err := sess.SQL(sql.String(), params...).Find(&items); err != nil {
|
||||||
items = nil
|
items = nil
|
||||||
return err
|
return err
|
||||||
@ -288,13 +294,13 @@ func getAccessControlFilter(user *user.SignedInUser) (string, []interface{}, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *SQLAnnotationRepo) Delete(ctx context.Context, params *annotations.DeleteParams) error {
|
func (r *SQLAnnotationRepo) Delete(ctx context.Context, params *annotations.DeleteParams) error {
|
||||||
return r.sql.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
|
return r.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||||
var (
|
var (
|
||||||
sql string
|
sql string
|
||||||
annoTagSQL string
|
annoTagSQL string
|
||||||
)
|
)
|
||||||
|
|
||||||
sqlog.Info("delete", "orgId", params.OrgId)
|
r.log.Info("delete", "orgId", params.OrgId)
|
||||||
if params.Id != 0 {
|
if params.Id != 0 {
|
||||||
annoTagSQL = "DELETE FROM annotation_tag WHERE annotation_id IN (SELECT id FROM annotation WHERE id = ? AND org_id = ?)"
|
annoTagSQL = "DELETE FROM annotation_tag WHERE annotation_id IN (SELECT id FROM annotation WHERE id = ? AND org_id = ?)"
|
||||||
sql = "DELETE FROM annotation WHERE id = ? AND org_id = ?"
|
sql = "DELETE FROM annotation WHERE id = ? AND org_id = ?"
|
||||||
@ -323,17 +329,17 @@ func (r *SQLAnnotationRepo) Delete(ctx context.Context, params *annotations.Dele
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *SQLAnnotationRepo) FindTags(ctx context.Context, query *annotations.TagsQuery) (annotations.FindTagsResult, error) {
|
func (r *SQLAnnotationRepo) GetTags(ctx context.Context, query *annotations.TagsQuery) (annotations.FindTagsResult, error) {
|
||||||
var items []*annotations.Tag
|
var items []*annotations.Tag
|
||||||
err := r.sql.WithDbSession(ctx, func(dbSession *DBSession) error {
|
err := r.db.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
||||||
if query.Limit == 0 {
|
if query.Limit == 0 {
|
||||||
query.Limit = 100
|
query.Limit = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
var sql bytes.Buffer
|
var sql bytes.Buffer
|
||||||
params := make([]interface{}, 0)
|
params := make([]interface{}, 0)
|
||||||
tagKey := `tag.` + dialect.Quote("key")
|
tagKey := `tag.` + r.db.GetDialect().Quote("key")
|
||||||
tagValue := `tag.` + dialect.Quote("value")
|
tagValue := `tag.` + r.db.GetDialect().Quote("value")
|
||||||
|
|
||||||
sql.WriteString(`
|
sql.WriteString(`
|
||||||
SELECT
|
SELECT
|
||||||
@ -347,12 +353,12 @@ func (r *SQLAnnotationRepo) FindTags(ctx context.Context, query *annotations.Tag
|
|||||||
sql.WriteString(`WHERE EXISTS(SELECT 1 FROM annotation WHERE annotation.id = annotation_tag.annotation_id AND annotation.org_id = ?)`)
|
sql.WriteString(`WHERE EXISTS(SELECT 1 FROM annotation WHERE annotation.id = annotation_tag.annotation_id AND annotation.org_id = ?)`)
|
||||||
params = append(params, query.OrgID)
|
params = append(params, query.OrgID)
|
||||||
|
|
||||||
sql.WriteString(` AND (` + tagKey + ` ` + dialect.LikeStr() + ` ? OR ` + tagValue + ` ` + dialect.LikeStr() + ` ?)`)
|
sql.WriteString(` AND (` + tagKey + ` ` + r.db.GetDialect().LikeStr() + ` ? OR ` + tagValue + ` ` + r.db.GetDialect().LikeStr() + ` ?)`)
|
||||||
params = append(params, `%`+query.Tag+`%`, `%`+query.Tag+`%`)
|
params = append(params, `%`+query.Tag+`%`, `%`+query.Tag+`%`)
|
||||||
|
|
||||||
sql.WriteString(` GROUP BY ` + tagKey + `,` + tagValue)
|
sql.WriteString(` GROUP BY ` + tagKey + `,` + tagValue)
|
||||||
sql.WriteString(` ORDER BY ` + tagKey + `,` + tagValue)
|
sql.WriteString(` ORDER BY ` + tagKey + `,` + tagValue)
|
||||||
sql.WriteString(` ` + dialect.Limit(query.Limit))
|
sql.WriteString(` ` + r.db.GetDialect().Limit(query.Limit))
|
||||||
|
|
||||||
err := dbSession.SQL(sql.String(), params...).Find(&items)
|
err := dbSession.SQL(sql.String(), params...).Find(&items)
|
||||||
return err
|
return err
|
@ -1,12 +1,14 @@
|
|||||||
package sqlstore_test
|
package annotationsimpl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -25,7 +27,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
t.Skip("skipping integration test")
|
t.Skip("skipping integration test")
|
||||||
}
|
}
|
||||||
sql := sqlstore.InitTestDB(t)
|
sql := sqlstore.InitTestDB(t)
|
||||||
repo := sqlstore.NewSQLAnnotationRepo(sql)
|
repo := SQLAnnotationRepo{db: sql, cfg: setting.NewCfg(), log: log.New("annotation.test")}
|
||||||
|
|
||||||
testUser := &user.SignedInUser{
|
testUser := &user.SignedInUser{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
@ -81,7 +83,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
Epoch: 10,
|
Epoch: 10,
|
||||||
Tags: []string{"outage", "error", "type:outage", "server:server-1"},
|
Tags: []string{"outage", "error", "type:outage", "server:server-1"},
|
||||||
}
|
}
|
||||||
err = repo.Save(annotation)
|
err = repo.Add(context.Background(), annotation)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Greater(t, annotation.Id, int64(0))
|
assert.Greater(t, annotation.Id, int64(0))
|
||||||
assert.Equal(t, annotation.Epoch, annotation.EpochEnd)
|
assert.Equal(t, annotation.Epoch, annotation.EpochEnd)
|
||||||
@ -96,7 +98,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
EpochEnd: 20,
|
EpochEnd: 20,
|
||||||
Tags: []string{"outage", "error", "type:outage", "server:server-1"},
|
Tags: []string{"outage", "error", "type:outage", "server:server-1"},
|
||||||
}
|
}
|
||||||
err = repo.Save(annotation2)
|
err = repo.Add(context.Background(), annotation2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Greater(t, annotation2.Id, int64(0))
|
assert.Greater(t, annotation2.Id, int64(0))
|
||||||
assert.Equal(t, int64(20), annotation2.Epoch)
|
assert.Equal(t, int64(20), annotation2.Epoch)
|
||||||
@ -110,7 +112,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
Epoch: 15,
|
Epoch: 15,
|
||||||
Tags: []string{"deploy"},
|
Tags: []string{"deploy"},
|
||||||
}
|
}
|
||||||
err = repo.Save(organizationAnnotation1)
|
err = repo.Add(context.Background(), organizationAnnotation1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Greater(t, organizationAnnotation1.Id, int64(0))
|
assert.Greater(t, organizationAnnotation1.Id, int64(0))
|
||||||
|
|
||||||
@ -122,11 +124,11 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
Epoch: 17,
|
Epoch: 17,
|
||||||
Tags: []string{"rollback"},
|
Tags: []string{"rollback"},
|
||||||
}
|
}
|
||||||
err = repo.Save(globalAnnotation2)
|
err = repo.Add(context.Background(), globalAnnotation2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Greater(t, globalAnnotation2.Id, int64(0))
|
assert.Greater(t, globalAnnotation2.Id, int64(0))
|
||||||
t.Run("Can query for annotation by dashboard id", func(t *testing.T) {
|
t.Run("Can query for annotation by dashboard id", func(t *testing.T) {
|
||||||
items, err := repo.Find(context.Background(), &annotations.ItemQuery{
|
items, err := repo.Get(context.Background(), &annotations.ItemQuery{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
DashboardId: dashboard.Id,
|
DashboardId: dashboard.Id,
|
||||||
From: 0,
|
From: 0,
|
||||||
@ -145,7 +147,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Can query for annotation by id", func(t *testing.T) {
|
t.Run("Can query for annotation by id", func(t *testing.T) {
|
||||||
items, err := repo.Find(context.Background(), &annotations.ItemQuery{
|
items, err := repo.Get(context.Background(), &annotations.ItemQuery{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
AnnotationId: annotation2.Id,
|
AnnotationId: annotation2.Id,
|
||||||
SignedInUser: testUser,
|
SignedInUser: testUser,
|
||||||
@ -156,7 +158,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should not find any when item is outside time range", func(t *testing.T) {
|
t.Run("Should not find any when item is outside time range", func(t *testing.T) {
|
||||||
items, err := repo.Find(context.Background(), &annotations.ItemQuery{
|
items, err := repo.Get(context.Background(), &annotations.ItemQuery{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
DashboardId: 1,
|
DashboardId: 1,
|
||||||
From: 12,
|
From: 12,
|
||||||
@ -168,7 +170,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should not find one when tag filter does not match", func(t *testing.T) {
|
t.Run("Should not find one when tag filter does not match", func(t *testing.T) {
|
||||||
items, err := repo.Find(context.Background(), &annotations.ItemQuery{
|
items, err := repo.Get(context.Background(), &annotations.ItemQuery{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
DashboardId: 1,
|
DashboardId: 1,
|
||||||
From: 1,
|
From: 1,
|
||||||
@ -181,7 +183,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should not find one when type filter does not match", func(t *testing.T) {
|
t.Run("Should not find one when type filter does not match", func(t *testing.T) {
|
||||||
items, err := repo.Find(context.Background(), &annotations.ItemQuery{
|
items, err := repo.Get(context.Background(), &annotations.ItemQuery{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
DashboardId: 1,
|
DashboardId: 1,
|
||||||
From: 1,
|
From: 1,
|
||||||
@ -194,7 +196,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should find one when all tag filters does match", func(t *testing.T) {
|
t.Run("Should find one when all tag filters does match", func(t *testing.T) {
|
||||||
items, err := repo.Find(context.Background(), &annotations.ItemQuery{
|
items, err := repo.Get(context.Background(), &annotations.ItemQuery{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
DashboardId: 1,
|
DashboardId: 1,
|
||||||
From: 1,
|
From: 1,
|
||||||
@ -207,7 +209,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should find two annotations using partial match", func(t *testing.T) {
|
t.Run("Should find two annotations using partial match", func(t *testing.T) {
|
||||||
items, err := repo.Find(context.Background(), &annotations.ItemQuery{
|
items, err := repo.Get(context.Background(), &annotations.ItemQuery{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
From: 1,
|
From: 1,
|
||||||
To: 25,
|
To: 25,
|
||||||
@ -220,7 +222,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should find one when all key value tag filters does match", func(t *testing.T) {
|
t.Run("Should find one when all key value tag filters does match", func(t *testing.T) {
|
||||||
items, err := repo.Find(context.Background(), &annotations.ItemQuery{
|
items, err := repo.Get(context.Background(), &annotations.ItemQuery{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
DashboardId: 1,
|
DashboardId: 1,
|
||||||
From: 1,
|
From: 1,
|
||||||
@ -240,7 +242,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
To: 15,
|
To: 15,
|
||||||
SignedInUser: testUser,
|
SignedInUser: testUser,
|
||||||
}
|
}
|
||||||
items, err := repo.Find(context.Background(), query)
|
items, err := repo.Get(context.Background(), query)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
annotationId := items[0].Id
|
annotationId := items[0].Id
|
||||||
@ -252,7 +254,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
items, err = repo.Find(context.Background(), query)
|
items, err = repo.Get(context.Background(), query)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, annotationId, items[0].Id)
|
assert.Equal(t, annotationId, items[0].Id)
|
||||||
@ -268,7 +270,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
To: 15,
|
To: 15,
|
||||||
SignedInUser: testUser,
|
SignedInUser: testUser,
|
||||||
}
|
}
|
||||||
items, err := repo.Find(context.Background(), query)
|
items, err := repo.Get(context.Background(), query)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
annotationId := items[0].Id
|
annotationId := items[0].Id
|
||||||
@ -280,7 +282,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
items, err = repo.Find(context.Background(), query)
|
items, err = repo.Get(context.Background(), query)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, annotationId, items[0].Id)
|
assert.Equal(t, annotationId, items[0].Id)
|
||||||
@ -297,14 +299,14 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
To: 15,
|
To: 15,
|
||||||
SignedInUser: testUser,
|
SignedInUser: testUser,
|
||||||
}
|
}
|
||||||
items, err := repo.Find(context.Background(), query)
|
items, err := repo.Get(context.Background(), query)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
annotationId := items[0].Id
|
annotationId := items[0].Id
|
||||||
err = repo.Delete(context.Background(), &annotations.DeleteParams{Id: annotationId, OrgId: 1})
|
err = repo.Delete(context.Background(), &annotations.DeleteParams{Id: annotationId, OrgId: 1})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
items, err = repo.Find(context.Background(), query)
|
items, err = repo.Get(context.Background(), query)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Empty(t, items)
|
assert.Empty(t, items)
|
||||||
})
|
})
|
||||||
@ -320,7 +322,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
Tags: []string{"test"},
|
Tags: []string{"test"},
|
||||||
PanelId: 20,
|
PanelId: 20,
|
||||||
}
|
}
|
||||||
err = repo.Save(annotation3)
|
err = repo.Add(context.Background(), annotation3)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
query := &annotations.ItemQuery{
|
query := &annotations.ItemQuery{
|
||||||
@ -328,7 +330,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
AnnotationId: annotation3.Id,
|
AnnotationId: annotation3.Id,
|
||||||
SignedInUser: testUser,
|
SignedInUser: testUser,
|
||||||
}
|
}
|
||||||
items, err := repo.Find(context.Background(), query)
|
items, err := repo.Get(context.Background(), query)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
dashboardId := items[0].DashboardId
|
dashboardId := items[0].DashboardId
|
||||||
@ -336,13 +338,13 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
err = repo.Delete(context.Background(), &annotations.DeleteParams{DashboardId: dashboardId, PanelId: panelId, OrgId: 1})
|
err = repo.Delete(context.Background(), &annotations.DeleteParams{DashboardId: dashboardId, PanelId: panelId, OrgId: 1})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
items, err = repo.Find(context.Background(), query)
|
items, err = repo.Get(context.Background(), query)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Empty(t, items)
|
assert.Empty(t, items)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should find tags by key", func(t *testing.T) {
|
t.Run("Should find tags by key", func(t *testing.T) {
|
||||||
result, err := repo.FindTags(context.Background(), &annotations.TagsQuery{
|
result, err := repo.GetTags(context.Background(), &annotations.TagsQuery{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
Tag: "server",
|
Tag: "server",
|
||||||
})
|
})
|
||||||
@ -353,7 +355,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should find tags by value", func(t *testing.T) {
|
t.Run("Should find tags by value", func(t *testing.T) {
|
||||||
result, err := repo.FindTags(context.Background(), &annotations.TagsQuery{
|
result, err := repo.GetTags(context.Background(), &annotations.TagsQuery{
|
||||||
OrgID: 1,
|
OrgID: 1,
|
||||||
Tag: "outage",
|
Tag: "outage",
|
||||||
})
|
})
|
||||||
@ -366,7 +368,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should not find tags in other org", func(t *testing.T) {
|
t.Run("Should not find tags in other org", func(t *testing.T) {
|
||||||
result, err := repo.FindTags(context.Background(), &annotations.TagsQuery{
|
result, err := repo.GetTags(context.Background(), &annotations.TagsQuery{
|
||||||
OrgID: 0,
|
OrgID: 0,
|
||||||
Tag: "server-1",
|
Tag: "server-1",
|
||||||
})
|
})
|
||||||
@ -375,7 +377,7 @@ func TestIntegrationAnnotations(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Should not find tags that do not exist", func(t *testing.T) {
|
t.Run("Should not find tags that do not exist", func(t *testing.T) {
|
||||||
result, err := repo.FindTags(context.Background(), &annotations.TagsQuery{
|
result, err := repo.GetTags(context.Background(), &annotations.TagsQuery{
|
||||||
OrgID: 0,
|
OrgID: 0,
|
||||||
Tag: "unknown:tag",
|
Tag: "unknown:tag",
|
||||||
})
|
})
|
||||||
@ -390,7 +392,7 @@ func TestIntegrationAnnotationListingWithRBAC(t *testing.T) {
|
|||||||
t.Skip("skipping integration test")
|
t.Skip("skipping integration test")
|
||||||
}
|
}
|
||||||
sql := sqlstore.InitTestDB(t, sqlstore.InitTestDBOpt{})
|
sql := sqlstore.InitTestDB(t, sqlstore.InitTestDBOpt{})
|
||||||
repo := sqlstore.NewSQLAnnotationRepo(sql)
|
repo := SQLAnnotationRepo{db: sql, cfg: setting.NewCfg(), log: log.New("annotation.test")}
|
||||||
dashboardStore := dashboardstore.ProvideDashboardStore(sql, featuremgmt.WithFeatures())
|
dashboardStore := dashboardstore.ProvideDashboardStore(sql, featuremgmt.WithFeatures())
|
||||||
|
|
||||||
testDashboard1 := models.SaveDashboardCommand{
|
testDashboard1 := models.SaveDashboardCommand{
|
||||||
@ -419,7 +421,7 @@ func TestIntegrationAnnotationListingWithRBAC(t *testing.T) {
|
|||||||
DashboardId: 1,
|
DashboardId: 1,
|
||||||
Epoch: 10,
|
Epoch: 10,
|
||||||
}
|
}
|
||||||
err = repo.Save(dash1Annotation)
|
err = repo.Add(context.Background(), dash1Annotation)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
dash2Annotation := &annotations.Item{
|
dash2Annotation := &annotations.Item{
|
||||||
@ -427,14 +429,14 @@ func TestIntegrationAnnotationListingWithRBAC(t *testing.T) {
|
|||||||
DashboardId: 2,
|
DashboardId: 2,
|
||||||
Epoch: 10,
|
Epoch: 10,
|
||||||
}
|
}
|
||||||
err = repo.Save(dash2Annotation)
|
err = repo.Add(context.Background(), dash2Annotation)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
organizationAnnotation := &annotations.Item{
|
organizationAnnotation := &annotations.Item{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
Epoch: 10,
|
Epoch: 10,
|
||||||
}
|
}
|
||||||
err = repo.Save(organizationAnnotation)
|
err = repo.Add(context.Background(), organizationAnnotation)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
user := &user.SignedInUser{
|
user := &user.SignedInUser{
|
||||||
@ -501,7 +503,7 @@ func TestIntegrationAnnotationListingWithRBAC(t *testing.T) {
|
|||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.description, func(t *testing.T) {
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
user.Permissions = map[int64]map[string][]string{1: tc.permissions}
|
user.Permissions = map[int64]map[string][]string{1: tc.permissions}
|
||||||
results, err := repo.Find(context.Background(), &annotations.ItemQuery{
|
results, err := repo.Get(context.Background(), &annotations.ItemQuery{
|
||||||
OrgId: 1,
|
OrgId: 1,
|
||||||
SignedInUser: user,
|
SignedInUser: user,
|
||||||
})
|
})
|
83
pkg/services/annotations/annotationstest/fake.go
Normal file
83
pkg/services/annotations/annotationstest/fake.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package annotationstest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fakeAnnotationsRepo struct {
|
||||||
|
mtx sync.Mutex
|
||||||
|
annotations map[int64]annotations.Item
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFakeAnnotationsRepo() *fakeAnnotationsRepo {
|
||||||
|
return &fakeAnnotationsRepo{
|
||||||
|
annotations: map[int64]annotations.Item{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *fakeAnnotationsRepo) Delete(_ context.Context, params *annotations.DeleteParams) error {
|
||||||
|
repo.mtx.Lock()
|
||||||
|
defer repo.mtx.Unlock()
|
||||||
|
|
||||||
|
if params.Id != 0 {
|
||||||
|
delete(repo.annotations, params.Id)
|
||||||
|
} else {
|
||||||
|
for _, v := range repo.annotations {
|
||||||
|
if params.DashboardId == v.DashboardId && params.PanelId == v.PanelId {
|
||||||
|
delete(repo.annotations, v.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *fakeAnnotationsRepo) Save(ctx context.Context, item *annotations.Item) error {
|
||||||
|
repo.mtx.Lock()
|
||||||
|
defer repo.mtx.Unlock()
|
||||||
|
|
||||||
|
if item.Id == 0 {
|
||||||
|
item.Id = int64(len(repo.annotations) + 1)
|
||||||
|
}
|
||||||
|
repo.annotations[item.Id] = *item
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *fakeAnnotationsRepo) Update(_ context.Context, item *annotations.Item) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *fakeAnnotationsRepo) Find(_ context.Context, query *annotations.ItemQuery) ([]*annotations.ItemDTO, error) {
|
||||||
|
repo.mtx.Lock()
|
||||||
|
defer repo.mtx.Unlock()
|
||||||
|
|
||||||
|
if annotation, has := repo.annotations[query.AnnotationId]; has {
|
||||||
|
return []*annotations.ItemDTO{{Id: annotation.Id, DashboardId: annotation.DashboardId}}, nil
|
||||||
|
}
|
||||||
|
annotations := []*annotations.ItemDTO{{Id: 1, DashboardId: 0}}
|
||||||
|
return annotations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *fakeAnnotationsRepo) FindTags(_ context.Context, query *annotations.TagsQuery) (annotations.FindTagsResult, error) {
|
||||||
|
result := annotations.FindTagsResult{
|
||||||
|
Tags: []*annotations.TagsDTO{},
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *fakeAnnotationsRepo) Len() int {
|
||||||
|
repo.mtx.Lock()
|
||||||
|
defer repo.mtx.Unlock()
|
||||||
|
|
||||||
|
return len(repo.annotations)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *fakeAnnotationsRepo) Items() map[int64]annotations.Item {
|
||||||
|
repo.mtx.Lock()
|
||||||
|
defer repo.mtx.Unlock()
|
||||||
|
|
||||||
|
return repo.annotations
|
||||||
|
}
|
135
pkg/services/annotations/models.go
Normal file
135
pkg/services/annotations/models.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package annotations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ItemQuery struct {
|
||||||
|
OrgId int64 `json:"orgId"`
|
||||||
|
From int64 `json:"from"`
|
||||||
|
To int64 `json:"to"`
|
||||||
|
UserId int64 `json:"userId"`
|
||||||
|
AlertId int64 `json:"alertId"`
|
||||||
|
DashboardId int64 `json:"dashboardId"`
|
||||||
|
DashboardUid string `json:"dashboardUID"`
|
||||||
|
PanelId int64 `json:"panelId"`
|
||||||
|
AnnotationId int64 `json:"annotationId"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
MatchAny bool `json:"matchAny"`
|
||||||
|
SignedInUser *user.SignedInUser
|
||||||
|
|
||||||
|
Limit int64 `json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagsQuery is the query for a tags search.
|
||||||
|
type TagsQuery struct {
|
||||||
|
OrgID int64 `json:"orgId"`
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
|
||||||
|
Limit int64 `json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag is the DB result of a tags search.
|
||||||
|
type Tag struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
Count int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagsDTO is the frontend DTO for Tag.
|
||||||
|
type TagsDTO struct {
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
Count int64 `json:"count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindTagsResult is the result of a tags search.
|
||||||
|
type FindTagsResult struct {
|
||||||
|
Tags []*TagsDTO `json:"tags"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAnnotationTagsResponse is a response struct for FindTagsResult.
|
||||||
|
type GetAnnotationTagsResponse struct {
|
||||||
|
Result FindTagsResult `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteParams struct {
|
||||||
|
OrgId int64
|
||||||
|
Id int64
|
||||||
|
DashboardId int64
|
||||||
|
PanelId int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Item struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
OrgId int64 `json:"orgId"`
|
||||||
|
UserId int64 `json:"userId"`
|
||||||
|
DashboardId int64 `json:"dashboardId"`
|
||||||
|
PanelId int64 `json:"panelId"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
AlertId int64 `json:"alertId"`
|
||||||
|
PrevState string `json:"prevState"`
|
||||||
|
NewState string `json:"newState"`
|
||||||
|
Epoch int64 `json:"epoch"`
|
||||||
|
EpochEnd int64 `json:"epochEnd"`
|
||||||
|
Created int64 `json:"created"`
|
||||||
|
Updated int64 `json:"updated"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Data *simplejson.Json `json:"data"`
|
||||||
|
|
||||||
|
// needed until we remove it from db
|
||||||
|
Type string
|
||||||
|
Title string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i Item) TableName() string {
|
||||||
|
return "annotation"
|
||||||
|
}
|
||||||
|
|
||||||
|
type ItemDTO struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
AlertId int64 `json:"alertId"`
|
||||||
|
AlertName string `json:"alertName"`
|
||||||
|
DashboardId int64 `json:"dashboardId"`
|
||||||
|
DashboardUID *string `json:"dashboardUID"`
|
||||||
|
PanelId int64 `json:"panelId"`
|
||||||
|
UserId int64 `json:"userId"`
|
||||||
|
NewState string `json:"newState"`
|
||||||
|
PrevState string `json:"prevState"`
|
||||||
|
Created int64 `json:"created"`
|
||||||
|
Updated int64 `json:"updated"`
|
||||||
|
Time int64 `json:"time"`
|
||||||
|
TimeEnd int64 `json:"timeEnd"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Login string `json:"login"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
AvatarUrl string `json:"avatarUrl"`
|
||||||
|
Data *simplejson.Json `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type annotationType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Organization annotationType = iota
|
||||||
|
Dashboard
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a annotationType) String() string {
|
||||||
|
switch a {
|
||||||
|
case Organization:
|
||||||
|
return "organization"
|
||||||
|
case Dashboard:
|
||||||
|
return "dashboard"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (annotation *ItemDTO) GetType() annotationType {
|
||||||
|
if annotation.DashboardId != 0 {
|
||||||
|
return Dashboard
|
||||||
|
}
|
||||||
|
return Organization
|
||||||
|
}
|
@ -19,12 +19,14 @@ type PermissionChecker struct {
|
|||||||
features featuremgmt.FeatureToggles
|
features featuremgmt.FeatureToggles
|
||||||
accessControl accesscontrol.AccessControl
|
accessControl accesscontrol.AccessControl
|
||||||
dashboardService dashboards.DashboardService
|
dashboardService dashboards.DashboardService
|
||||||
|
annotationsRepo annotations.Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPermissionChecker(sqlStore *sqlstore.SQLStore, features featuremgmt.FeatureToggles,
|
func NewPermissionChecker(sqlStore *sqlstore.SQLStore, features featuremgmt.FeatureToggles,
|
||||||
accessControl accesscontrol.AccessControl, dashboardService dashboards.DashboardService,
|
accessControl accesscontrol.AccessControl, dashboardService dashboards.DashboardService,
|
||||||
|
annotationsRepo annotations.Repository,
|
||||||
) *PermissionChecker {
|
) *PermissionChecker {
|
||||||
return &PermissionChecker{sqlStore: sqlStore, features: features, accessControl: accessControl}
|
return &PermissionChecker{sqlStore: sqlStore, features: features, accessControl: accessControl, annotationsRepo: annotationsRepo}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PermissionChecker) getDashboardByUid(ctx context.Context, orgID int64, uid string) (*models.Dashboard, error) {
|
func (c *PermissionChecker) getDashboardByUid(ctx context.Context, orgID int64, uid string) (*models.Dashboard, error) {
|
||||||
@ -63,12 +65,11 @@ func (c *PermissionChecker) CheckReadPermissions(ctx context.Context, orgId int6
|
|||||||
if !c.features.IsEnabled(featuremgmt.FlagAnnotationComments) {
|
if !c.features.IsEnabled(featuremgmt.FlagAnnotationComments) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
repo := annotations.GetRepository()
|
|
||||||
annotationID, err := strconv.ParseInt(objectID, 10, 64)
|
annotationID, err := strconv.ParseInt(objectID, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
items, err := repo.Find(ctx, &annotations.ItemQuery{AnnotationId: annotationID, OrgId: orgId, SignedInUser: signedInUser})
|
items, err := c.annotationsRepo.Find(ctx, &annotations.ItemQuery{AnnotationId: annotationID, OrgId: orgId, SignedInUser: signedInUser})
|
||||||
if err != nil || len(items) != 1 {
|
if err != nil || len(items) != 1 {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@ -116,12 +117,11 @@ func (c *PermissionChecker) CheckWritePermissions(ctx context.Context, orgId int
|
|||||||
return canEdit, err
|
return canEdit, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repo := annotations.GetRepository()
|
|
||||||
annotationID, err := strconv.ParseInt(objectID, 10, 64)
|
annotationID, err := strconv.ParseInt(objectID, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
items, err := repo.Find(ctx, &annotations.ItemQuery{AnnotationId: annotationID, OrgId: orgId, SignedInUser: signedInUser})
|
items, err := c.annotationsRepo.Find(ctx, &annotations.ItemQuery{AnnotationId: annotationID, OrgId: orgId, SignedInUser: signedInUser})
|
||||||
if err != nil || len(items) != 1 {
|
if err != nil || len(items) != 1 {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
"github.com/grafana/grafana/pkg/services/comments/commentmodel"
|
"github.com/grafana/grafana/pkg/services/comments/commentmodel"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
@ -24,7 +25,7 @@ type Service struct {
|
|||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg, store *sqlstore.SQLStore, live *live.GrafanaLive,
|
func ProvideService(cfg *setting.Cfg, store *sqlstore.SQLStore, live *live.GrafanaLive,
|
||||||
features featuremgmt.FeatureToggles, accessControl accesscontrol.AccessControl,
|
features featuremgmt.FeatureToggles, accessControl accesscontrol.AccessControl,
|
||||||
dashboardService dashboards.DashboardService, userService user.Service) *Service {
|
dashboardService dashboards.DashboardService, userService user.Service, annotationsRepo annotations.Repository) *Service {
|
||||||
s := &Service{
|
s := &Service{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
live: live,
|
live: live,
|
||||||
@ -32,7 +33,7 @@ func ProvideService(cfg *setting.Cfg, store *sqlstore.SQLStore, live *live.Grafa
|
|||||||
storage: &sqlStorage{
|
storage: &sqlStorage{
|
||||||
sql: store,
|
sql: store,
|
||||||
},
|
},
|
||||||
permissions: commentmodel.NewPermissionChecker(store, features, accessControl, dashboardService),
|
permissions: commentmodel.NewPermissionChecker(store, features, accessControl, dashboardService, annotationsRepo),
|
||||||
userService: userService,
|
userService: userService,
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
"github.com/grafana/grafana/pkg/plugins/plugincontext"
|
"github.com/grafana/grafana/pkg/plugins/plugincontext"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
"github.com/grafana/grafana/pkg/services/comments/commentmodel"
|
"github.com/grafana/grafana/pkg/services/comments/commentmodel"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
@ -75,7 +76,7 @@ func ProvideService(plugCtxProvider *plugincontext.Provider, cfg *setting.Cfg, r
|
|||||||
pluginStore plugins.Store, cacheService *localcache.CacheService,
|
pluginStore plugins.Store, cacheService *localcache.CacheService,
|
||||||
dataSourceCache datasources.CacheService, sqlStore *sqlstore.SQLStore, secretsService secrets.Service,
|
dataSourceCache datasources.CacheService, sqlStore *sqlstore.SQLStore, secretsService secrets.Service,
|
||||||
usageStatsService usagestats.Service, queryDataService *query.Service, toggles featuremgmt.FeatureToggles,
|
usageStatsService usagestats.Service, queryDataService *query.Service, toggles featuremgmt.FeatureToggles,
|
||||||
accessControl accesscontrol.AccessControl, dashboardService dashboards.DashboardService) (*GrafanaLive, error) {
|
accessControl accesscontrol.AccessControl, dashboardService dashboards.DashboardService, annotationsRepo annotations.Repository) (*GrafanaLive, error) {
|
||||||
g := &GrafanaLive{
|
g := &GrafanaLive{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
Features: toggles,
|
Features: toggles,
|
||||||
@ -247,7 +248,7 @@ func ProvideService(plugCtxProvider *plugincontext.Provider, cfg *setting.Cfg, r
|
|||||||
g.GrafanaScope.Dashboards = dash
|
g.GrafanaScope.Dashboards = dash
|
||||||
g.GrafanaScope.Features["dashboard"] = dash
|
g.GrafanaScope.Features["dashboard"] = dash
|
||||||
g.GrafanaScope.Features["broadcast"] = features.NewBroadcastRunner(g.storage)
|
g.GrafanaScope.Features["broadcast"] = features.NewBroadcastRunner(g.storage)
|
||||||
g.GrafanaScope.Features["comment"] = features.NewCommentHandler(commentmodel.NewPermissionChecker(g.SQLStore, g.Features, accessControl, dashboardService))
|
g.GrafanaScope.Features["comment"] = features.NewCommentHandler(commentmodel.NewPermissionChecker(g.SQLStore, g.Features, accessControl, dashboardService, annotationsRepo))
|
||||||
|
|
||||||
g.surveyCaller = survey.NewCaller(managedStreamRunner, node)
|
g.surveyCaller = survey.NewCaller(managedStreamRunner, node)
|
||||||
err = g.surveyCaller.SetupHandlers()
|
err = g.surveyCaller.SetupHandlers()
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/datasourceproxy"
|
"github.com/grafana/grafana/pkg/services/datasourceproxy"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
@ -41,7 +42,7 @@ func ProvideService(cfg *setting.Cfg, dataSourceCache datasources.CacheService,
|
|||||||
sqlStore *sqlstore.SQLStore, kvStore kvstore.KVStore, expressionService *expr.Service, dataProxy *datasourceproxy.DataSourceProxyService,
|
sqlStore *sqlstore.SQLStore, kvStore kvstore.KVStore, expressionService *expr.Service, dataProxy *datasourceproxy.DataSourceProxyService,
|
||||||
quotaService quota.Service, secretsService secrets.Service, notificationService notifications.Service, m *metrics.NGAlert,
|
quotaService quota.Service, secretsService secrets.Service, notificationService notifications.Service, m *metrics.NGAlert,
|
||||||
folderService dashboards.FolderService, ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, renderService rendering.Service,
|
folderService dashboards.FolderService, ac accesscontrol.AccessControl, dashboardService dashboards.DashboardService, renderService rendering.Service,
|
||||||
bus bus.Bus, accesscontrolService accesscontrol.Service) (*AlertNG, error) {
|
bus bus.Bus, accesscontrolService accesscontrol.Service, annotationsRepo annotations.Repository) (*AlertNG, error) {
|
||||||
ng := &AlertNG{
|
ng := &AlertNG{
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
DataSourceCache: dataSourceCache,
|
DataSourceCache: dataSourceCache,
|
||||||
@ -62,6 +63,7 @@ func ProvideService(cfg *setting.Cfg, dataSourceCache datasources.CacheService,
|
|||||||
renderService: renderService,
|
renderService: renderService,
|
||||||
bus: bus,
|
bus: bus,
|
||||||
accesscontrolService: accesscontrolService,
|
accesscontrolService: accesscontrolService,
|
||||||
|
annotationsRepo: annotationsRepo,
|
||||||
}
|
}
|
||||||
|
|
||||||
if ng.IsDisabled() {
|
if ng.IsDisabled() {
|
||||||
@ -102,6 +104,7 @@ type AlertNG struct {
|
|||||||
AlertsRouter *sender.AlertsRouter
|
AlertsRouter *sender.AlertsRouter
|
||||||
accesscontrol accesscontrol.AccessControl
|
accesscontrol accesscontrol.AccessControl
|
||||||
accesscontrolService accesscontrol.Service
|
accesscontrolService accesscontrol.Service
|
||||||
|
annotationsRepo annotations.Repository
|
||||||
|
|
||||||
bus bus.Bus
|
bus bus.Bus
|
||||||
}
|
}
|
||||||
@ -165,7 +168,7 @@ func (ng *AlertNG) init() error {
|
|||||||
AlertSender: alertsRouter,
|
AlertSender: alertsRouter,
|
||||||
}
|
}
|
||||||
|
|
||||||
stateManager := state.NewManager(ng.Log, ng.Metrics.GetStateMetrics(), appUrl, store, store, ng.dashboardService, ng.imageService, clk)
|
stateManager := state.NewManager(ng.Log, ng.Metrics.GetStateMetrics(), appUrl, store, store, ng.dashboardService, ng.imageService, clk, ng.annotationsRepo)
|
||||||
scheduler := schedule.NewScheduler(schedCfg, appUrl, stateManager)
|
scheduler := schedule.NewScheduler(schedCfg, appUrl, stateManager)
|
||||||
|
|
||||||
// if it is required to include folder title to the alerts, we need to subscribe to changes of alert title
|
// if it is required to include folder title to the alerts, we need to subscribe to changes of alert title
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
||||||
@ -112,7 +113,7 @@ func TestWarmStateCache(t *testing.T) {
|
|||||||
InstanceStore: dbstore,
|
InstanceStore: dbstore,
|
||||||
Metrics: testMetrics.GetSchedulerMetrics(),
|
Metrics: testMetrics.GetSchedulerMetrics(),
|
||||||
}
|
}
|
||||||
st := state.NewManager(schedCfg.Logger, testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clock.NewMock())
|
st := state.NewManager(schedCfg.Logger, testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clock.NewMock(), annotationstest.NewFakeAnnotationsRepo())
|
||||||
st.Warm(ctx)
|
st.Warm(ctx)
|
||||||
|
|
||||||
t.Run("instance cache has expected entries", func(t *testing.T) {
|
t.Run("instance cache has expected entries", func(t *testing.T) {
|
||||||
@ -166,7 +167,7 @@ func TestAlertingTicker(t *testing.T) {
|
|||||||
Metrics: testMetrics.GetSchedulerMetrics(),
|
Metrics: testMetrics.GetSchedulerMetrics(),
|
||||||
AlertSender: notifier,
|
AlertSender: notifier,
|
||||||
}
|
}
|
||||||
st := state.NewManager(schedCfg.Logger, testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clock.NewMock())
|
st := state.NewManager(schedCfg.Logger, testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clock.NewMock(), annotationstest.NewFakeAnnotationsRepo())
|
||||||
appUrl := &url.URL{
|
appUrl := &url.URL{
|
||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: "localhost",
|
Host: "localhost",
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/expr"
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/annotations"
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
@ -484,8 +484,6 @@ func TestSchedule_DeleteAlertRule(t *testing.T) {
|
|||||||
func setupScheduler(t *testing.T, rs *store.FakeRuleStore, is *store.FakeInstanceStore, registry *prometheus.Registry, senderMock *AlertsSenderMock, evalMock *eval.FakeEvaluator) *schedule {
|
func setupScheduler(t *testing.T, rs *store.FakeRuleStore, is *store.FakeInstanceStore, registry *prometheus.Registry, senderMock *AlertsSenderMock, evalMock *eval.FakeEvaluator) *schedule {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
fakeAnnoRepo := store.NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
mockedClock := clock.NewMock()
|
mockedClock := clock.NewMock()
|
||||||
logger := log.New("ngalert schedule test")
|
logger := log.New("ngalert schedule test")
|
||||||
|
|
||||||
@ -531,7 +529,7 @@ func setupScheduler(t *testing.T, rs *store.FakeRuleStore, is *store.FakeInstanc
|
|||||||
Metrics: m.GetSchedulerMetrics(),
|
Metrics: m.GetSchedulerMetrics(),
|
||||||
AlertSender: senderMock,
|
AlertSender: senderMock,
|
||||||
}
|
}
|
||||||
st := state.NewManager(schedCfg.Logger, m.GetStateMetrics(), nil, rs, is, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, mockedClock)
|
st := state.NewManager(schedCfg.Logger, m.GetStateMetrics(), nil, rs, is, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, mockedClock, annotationstest.NewFakeAnnotationsRepo())
|
||||||
return NewScheduler(schedCfg, appUrl, st)
|
return NewScheduler(schedCfg, appUrl, st)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,11 +45,12 @@ type Manager struct {
|
|||||||
instanceStore store.InstanceStore
|
instanceStore store.InstanceStore
|
||||||
dashboardService dashboards.DashboardService
|
dashboardService dashboards.DashboardService
|
||||||
imageService image.ImageService
|
imageService image.ImageService
|
||||||
|
AnnotationsRepo annotations.Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewManager(logger log.Logger, metrics *metrics.State, externalURL *url.URL,
|
func NewManager(logger log.Logger, metrics *metrics.State, externalURL *url.URL,
|
||||||
ruleStore store.RuleStore, instanceStore store.InstanceStore,
|
ruleStore store.RuleStore, instanceStore store.InstanceStore,
|
||||||
dashboardService dashboards.DashboardService, imageService image.ImageService, clock clock.Clock) *Manager {
|
dashboardService dashboards.DashboardService, imageService image.ImageService, clock clock.Clock, annotationsRepo annotations.Repository) *Manager {
|
||||||
manager := &Manager{
|
manager := &Manager{
|
||||||
cache: newCache(logger, metrics, externalURL),
|
cache: newCache(logger, metrics, externalURL),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
@ -61,6 +62,7 @@ func NewManager(logger log.Logger, metrics *metrics.State, externalURL *url.URL,
|
|||||||
dashboardService: dashboardService,
|
dashboardService: dashboardService,
|
||||||
imageService: imageService,
|
imageService: imageService,
|
||||||
clock: clock,
|
clock: clock,
|
||||||
|
AnnotationsRepo: annotationsRepo,
|
||||||
}
|
}
|
||||||
go manager.recordMetrics()
|
go manager.recordMetrics()
|
||||||
return manager
|
return manager
|
||||||
@ -419,8 +421,7 @@ func (st *Manager) annotateState(ctx context.Context, alertRule *ngModels.AlertR
|
|||||||
item.DashboardId = query.Result.Id
|
item.DashboardId = query.Result.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
annotationRepo := annotations.GetRepository()
|
if err := st.AnnotationsRepo.Save(ctx, item); err != nil {
|
||||||
if err := annotationRepo.Save(item); err != nil {
|
|
||||||
st.log.Error("error saving alert annotation", "alertRuleUID", alertRule.UID, "err", err.Error())
|
st.log.Error("error saving alert annotation", "alertRuleUID", alertRule.UID, "err", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/benbjohnson/clock"
|
"github.com/benbjohnson/clock"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/annotations"
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
||||||
@ -98,7 +98,7 @@ func Test_maybeNewImage(t *testing.T) {
|
|||||||
imageService := &CountingImageService{}
|
imageService := &CountingImageService{}
|
||||||
mgr := NewManager(log.NewNopLogger(), &metrics.State{}, nil,
|
mgr := NewManager(log.NewNopLogger(), &metrics.State{}, nil,
|
||||||
&store.FakeRuleStore{}, &store.FakeInstanceStore{},
|
&store.FakeRuleStore{}, &store.FakeInstanceStore{},
|
||||||
&dashboards.FakeDashboardService{}, imageService, clock.NewMock())
|
&dashboards.FakeDashboardService{}, imageService, clock.NewMock(), annotationstest.NewFakeAnnotationsRepo())
|
||||||
err := mgr.maybeTakeScreenshot(context.Background(), &ngmodels.AlertRule{}, test.state, test.oldState)
|
err := mgr.maybeTakeScreenshot(context.Background(), &ngmodels.AlertRule{}, test.state, test.oldState)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
if !test.shouldScreenshot {
|
if !test.shouldScreenshot {
|
||||||
@ -156,9 +156,7 @@ func TestIsItStale(t *testing.T) {
|
|||||||
func TestClose(t *testing.T) {
|
func TestClose(t *testing.T) {
|
||||||
instanceStore := &store.FakeInstanceStore{}
|
instanceStore := &store.FakeInstanceStore{}
|
||||||
clk := clock.New()
|
clk := clock.New()
|
||||||
st := NewManager(log.New("test_state_manager"), metrics.NewNGAlert(prometheus.NewPedanticRegistry()).GetStateMetrics(), nil, nil, instanceStore, &dashboards.FakeDashboardService{}, &image.NotAvailableImageService{}, clk)
|
st := NewManager(log.New("test_state_manager"), metrics.NewNGAlert(prometheus.NewPedanticRegistry()).GetStateMetrics(), nil, nil, instanceStore, &dashboards.FakeDashboardService{}, &image.NotAvailableImageService{}, clk, annotationstest.NewFakeAnnotationsRepo())
|
||||||
fakeAnnoRepo := store.NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
|
|
||||||
_, rules := ngmodels.GenerateUniqueAlertRules(10, ngmodels.AlertRuleGen())
|
_, rules := ngmodels.GenerateUniqueAlertRules(10, ngmodels.AlertRuleGen())
|
||||||
for _, rule := range rules {
|
for _, rule := range rules {
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/expr"
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/annotations"
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
||||||
@ -37,10 +38,8 @@ func TestDashboardAnnotations(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
_, dbstore := tests.SetupTestEnv(t, 1)
|
_, dbstore := tests.SetupTestEnv(t, 1)
|
||||||
|
|
||||||
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clock.New())
|
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
|
||||||
|
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clock.New(), fakeAnnoRepo)
|
||||||
fakeAnnoRepo := store.NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
|
|
||||||
const mainOrgID int64 = 1
|
const mainOrgID int64 = 1
|
||||||
|
|
||||||
@ -62,7 +61,7 @@ func TestDashboardAnnotations(t *testing.T) {
|
|||||||
sort.Strings(expected)
|
sort.Strings(expected)
|
||||||
require.Eventuallyf(t, func() bool {
|
require.Eventuallyf(t, func() bool {
|
||||||
var actual []string
|
var actual []string
|
||||||
for _, next := range fakeAnnoRepo.Items {
|
for _, next := range fakeAnnoRepo.Items() {
|
||||||
actual = append(actual, next.Text)
|
actual = append(actual, next.Text)
|
||||||
}
|
}
|
||||||
sort.Strings(actual)
|
sort.Strings(actual)
|
||||||
@ -1981,11 +1980,9 @@ func TestProcessEvalResults(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
st := state.NewManager(log.New("test_state_manager"), testMetrics.GetStateMetrics(), nil, nil, &store.FakeInstanceStore{}, &dashboards.FakeDashboardService{}, &image.NotAvailableImageService{}, clock.New())
|
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
|
||||||
|
st := state.NewManager(log.New("test_state_manager"), testMetrics.GetStateMetrics(), nil, nil, &store.FakeInstanceStore{}, &dashboards.FakeDashboardService{}, &image.NotAvailableImageService{}, clock.New(), fakeAnnoRepo)
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
fakeAnnoRepo := store.NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
|
|
||||||
for _, res := range tc.evalResults {
|
for _, res := range tc.evalResults {
|
||||||
_ = st.ProcessEvalResults(context.Background(), evaluationTime, tc.alertRule, res, data.Labels{
|
_ = st.ProcessEvalResults(context.Background(), evaluationTime, tc.alertRule, res, data.Labels{
|
||||||
"alertname": tc.alertRule.Title,
|
"alertname": tc.alertRule.Title,
|
||||||
@ -2005,16 +2002,14 @@ func TestProcessEvalResults(t *testing.T) {
|
|||||||
|
|
||||||
require.Eventuallyf(t, func() bool {
|
require.Eventuallyf(t, func() bool {
|
||||||
return tc.expectedAnnotations == fakeAnnoRepo.Len()
|
return tc.expectedAnnotations == fakeAnnoRepo.Len()
|
||||||
}, time.Second, 100*time.Millisecond, "%d annotations are present, expected %d. We have %+v", fakeAnnoRepo.Len(), tc.expectedAnnotations, printAllAnnotations(fakeAnnoRepo.Items))
|
}, time.Second, 100*time.Millisecond, "%d annotations are present, expected %d. We have %+v", fakeAnnoRepo.Len(), tc.expectedAnnotations, printAllAnnotations(fakeAnnoRepo.Items()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("should save state to database", func(t *testing.T) {
|
t.Run("should save state to database", func(t *testing.T) {
|
||||||
fakeAnnoRepo := store.NewFakeAnnotationsRepo()
|
|
||||||
annotations.SetRepository(fakeAnnoRepo)
|
|
||||||
instanceStore := &store.FakeInstanceStore{}
|
instanceStore := &store.FakeInstanceStore{}
|
||||||
clk := clock.New()
|
clk := clock.New()
|
||||||
st := state.NewManager(log.New("test_state_manager"), testMetrics.GetStateMetrics(), nil, nil, instanceStore, &dashboards.FakeDashboardService{}, &image.NotAvailableImageService{}, clk)
|
st := state.NewManager(log.New("test_state_manager"), testMetrics.GetStateMetrics(), nil, nil, instanceStore, &dashboards.FakeDashboardService{}, &image.NotAvailableImageService{}, clk, annotationstest.NewFakeAnnotationsRepo())
|
||||||
rule := models.AlertRuleGen()()
|
rule := models.AlertRuleGen()()
|
||||||
var results = eval.GenerateResults(rand.Intn(4)+1, eval.ResultGen(eval.WithEvaluatedAt(clk.Now())))
|
var results = eval.GenerateResults(rand.Intn(4)+1, eval.ResultGen(eval.WithEvaluatedAt(clk.Now())))
|
||||||
|
|
||||||
@ -2038,10 +2033,10 @@ func TestProcessEvalResults(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func printAllAnnotations(annos []*annotations.Item) string {
|
func printAllAnnotations(annos map[int64]annotations.Item) string {
|
||||||
str := "["
|
str := "["
|
||||||
for _, anno := range annos {
|
for _, anno := range annos {
|
||||||
str += fmt.Sprintf("%+v, ", *anno)
|
str += fmt.Sprintf("%+v, ", anno)
|
||||||
}
|
}
|
||||||
str += "]"
|
str += "]"
|
||||||
|
|
||||||
@ -2131,7 +2126,7 @@ func TestStaleResultsHandler(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clock.New())
|
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clock.New(), annotationstest.NewFakeAnnotationsRepo())
|
||||||
st.Warm(ctx)
|
st.Warm(ctx)
|
||||||
existingStatesForRule := st.GetStatesForRuleUID(rule.OrgID, rule.UID)
|
existingStatesForRule := st.GetStatesForRuleUID(rule.OrgID, rule.UID)
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/annotations"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
|
||||||
@ -481,47 +480,3 @@ func (f *FakeAdminConfigStore) UpdateAdminConfiguration(cmd UpdateAdminConfigura
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type FakeAnnotationsRepo struct {
|
|
||||||
mtx sync.Mutex
|
|
||||||
Items []*annotations.Item
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFakeAnnotationsRepo() *FakeAnnotationsRepo {
|
|
||||||
return &FakeAnnotationsRepo{
|
|
||||||
Items: make([]*annotations.Item, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *FakeAnnotationsRepo) Len() int {
|
|
||||||
repo.mtx.Lock()
|
|
||||||
defer repo.mtx.Unlock()
|
|
||||||
return len(repo.Items)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *FakeAnnotationsRepo) Delete(_ context.Context, params *annotations.DeleteParams) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *FakeAnnotationsRepo) Save(item *annotations.Item) error {
|
|
||||||
repo.mtx.Lock()
|
|
||||||
defer repo.mtx.Unlock()
|
|
||||||
repo.Items = append(repo.Items, item)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (repo *FakeAnnotationsRepo) Update(_ context.Context, item *annotations.Item) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *FakeAnnotationsRepo) Find(_ context.Context, query *annotations.ItemQuery) ([]*annotations.ItemDTO, error) {
|
|
||||||
annotations := []*annotations.ItemDTO{{Id: 1}}
|
|
||||||
return annotations, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repo *FakeAnnotationsRepo) FindTags(_ context.Context, query *annotations.TagsQuery) (annotations.FindTagsResult, error) {
|
|
||||||
result := annotations.FindTagsResult{
|
|
||||||
Tags: []*annotations.TagsDTO{},
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
databasestore "github.com/grafana/grafana/pkg/services/dashboards/database"
|
databasestore "github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||||
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
|
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
|
||||||
@ -81,7 +82,7 @@ func SetupTestEnv(t *testing.T, baseInterval time.Duration) (*ngalert.AlertNG, *
|
|||||||
|
|
||||||
ng, err := ngalert.ProvideService(
|
ng, err := ngalert.ProvideService(
|
||||||
cfg, nil, nil, routing.NewRouteRegister(), sqlStore, nil, nil, nil, nil,
|
cfg, nil, nil, routing.NewRouteRegister(), sqlStore, nil, nil, nil, nil,
|
||||||
secretsService, nil, m, folderService, ac, &dashboards.FakeDashboardService{}, nil, bus, ac,
|
secretsService, nil, m, folderService, ac, &dashboards.FakeDashboardService{}, nil, bus, ac, annotationstest.NewFakeAnnotationsRepo(),
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
return ng, &store.DBstore{
|
return ng, &store.DBstore{
|
||||||
|
@ -120,7 +120,6 @@ func newSQLStore(cfg *setting.Cfg, cacheService *localcache.CacheService, engine
|
|||||||
dialect = ss.Dialect
|
dialect = ss.Dialect
|
||||||
|
|
||||||
// Init repo instances
|
// Init repo instances
|
||||||
annotations.SetRepository(&SQLAnnotationRepo{sql: ss})
|
|
||||||
annotations.SetAnnotationCleaner(&AnnotationCleanupService{batchSize: ss.Cfg.AnnotationCleanupJobBatchSize, log: log.New("annotationcleaner"), sqlstore: ss})
|
annotations.SetAnnotationCleaner(&AnnotationCleanupService{batchSize: ss.Cfg.AnnotationCleanupJobBatchSize, log: log.New("annotationcleaner"), sqlstore: ss})
|
||||||
|
|
||||||
// if err := ss.Reset(); err != nil {
|
// if err := ss.Reset(); err != nil {
|
||||||
|
@ -2,7 +2,6 @@ package sqlstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
@ -10,8 +9,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
var timeNow = time.Now
|
|
||||||
|
|
||||||
type Store interface {
|
type Store interface {
|
||||||
GetAdminStats(ctx context.Context, query *models.GetAdminStatsQuery) error
|
GetAdminStats(ctx context.Context, query *models.GetAdminStatsQuery) error
|
||||||
GetAlertNotifiersUsageStats(ctx context.Context, query *models.GetAlertNotifierUsageStatsQuery) error
|
GetAlertNotifiersUsageStats(ctx context.Context, query *models.GetAlertNotifierUsageStatsQuery) error
|
||||||
|
Loading…
Reference in New Issue
Block a user