diff --git a/pkg/dashboards/ifaces.go b/pkg/dashboards/ifaces.go index 4699a6d9c16..101cd362676 100644 --- a/pkg/dashboards/ifaces.go +++ b/pkg/dashboards/ifaces.go @@ -6,6 +6,8 @@ import "github.com/grafana/grafana/pkg/models" type Store interface { // ValidateDashboardBeforeSave validates a dashboard before save. ValidateDashboardBeforeSave(dashboard *models.Dashboard, overwrite bool) (bool, error) + // GetFolderByTitle retrieves a dashboard by its title and is used by unified alerting + GetFolderByTitle(orgID int64, title string) (*models.Dashboard, error) GetProvisionedDataByDashboardID(dashboardID int64) (*models.DashboardProvisioning, error) GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error) SaveProvisionedDashboard(cmd models.SaveDashboardCommand, provisioning *models.DashboardProvisioning) (*models.Dashboard, error) diff --git a/pkg/services/dashboards/folder_service.go b/pkg/services/dashboards/folder_service.go index f087af4d870..4d3e1299362 100644 --- a/pkg/services/dashboards/folder_service.go +++ b/pkg/services/dashboards/folder_service.go @@ -16,7 +16,7 @@ type FolderService interface { GetFolders(limit int64) ([]*models.Folder, error) GetFolderByID(id int64) (*models.Folder, error) GetFolderByUID(uid string) (*models.Folder, error) - GetFolderBySlug(slug string) (*models.Folder, error) + GetFolderByTitle(title string) (*models.Folder, error) CreateFolder(title, uid string) (*models.Folder, error) UpdateFolder(uid string, cmd *models.UpdateFolderCommand) error DeleteFolder(uid string) (*models.Folder, error) @@ -97,9 +97,8 @@ func (dr *dashboardServiceImpl) GetFolderByUID(uid string) (*models.Folder, erro return dashToFolder(dashFolder), nil } -func (dr *dashboardServiceImpl) GetFolderBySlug(slug string) (*models.Folder, error) { - query := models.GetDashboardQuery{OrgId: dr.orgId, Slug: slug} - dashFolder, err := getFolder(query) +func (dr *dashboardServiceImpl) GetFolderByTitle(title string) (*models.Folder, error) { + dashFolder, err := dr.dashboardStore.GetFolderByTitle(dr.orgId, title) if err != nil { return nil, toFolderError(err) } diff --git a/pkg/services/ngalert/api/api_ruler.go b/pkg/services/ngalert/api/api_ruler.go index 6f27baf7f64..96d01ba5774 100644 --- a/pkg/services/ngalert/api/api_ruler.go +++ b/pkg/services/ngalert/api/api_ruler.go @@ -23,7 +23,7 @@ type RulerSrv struct { func (srv RulerSrv) RouteDeleteNamespaceRulesConfig(c *models.ReqContext) response.Response { namespace := c.Params(":Namespace") - namespaceUID, err := srv.store.GetNamespaceUIDBySlug(namespace, c.SignedInUser.OrgId, c.SignedInUser) + namespaceUID, err := srv.store.GetNamespaceUIDByTitle(namespace, c.SignedInUser.OrgId, c.SignedInUser) if err != nil { return response.Error(http.StatusInternalServerError, fmt.Sprintf("failed to get namespace: %s", namespace), err) } @@ -35,7 +35,7 @@ func (srv RulerSrv) RouteDeleteNamespaceRulesConfig(c *models.ReqContext) respon func (srv RulerSrv) RouteDeleteRuleGroupConfig(c *models.ReqContext) response.Response { namespace := c.Params(":Namespace") - namespaceUID, err := srv.store.GetNamespaceUIDBySlug(namespace, c.SignedInUser.OrgId, c.SignedInUser) + namespaceUID, err := srv.store.GetNamespaceUIDByTitle(namespace, c.SignedInUser.OrgId, c.SignedInUser) if err != nil { return response.Error(http.StatusInternalServerError, fmt.Sprintf("failed to get namespace: %s", namespace), err) } @@ -48,7 +48,7 @@ func (srv RulerSrv) RouteDeleteRuleGroupConfig(c *models.ReqContext) response.Re func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *models.ReqContext) response.Response { namespace := c.Params(":Namespace") - namespaceUID, err := srv.store.GetNamespaceUIDBySlug(namespace, c.SignedInUser.OrgId, c.SignedInUser) + namespaceUID, err := srv.store.GetNamespaceUIDByTitle(namespace, c.SignedInUser.OrgId, c.SignedInUser) if err != nil { return response.Error(http.StatusInternalServerError, fmt.Sprintf("failed to get namespace: %s", namespace), err) } @@ -89,7 +89,7 @@ func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *models.ReqContext) response. func (srv RulerSrv) RouteGetRulegGroupConfig(c *models.ReqContext) response.Response { namespace := c.Params(":Namespace") - namespaceUID, err := srv.store.GetNamespaceUIDBySlug(namespace, c.SignedInUser.OrgId, c.SignedInUser) + namespaceUID, err := srv.store.GetNamespaceUIDByTitle(namespace, c.SignedInUser.OrgId, c.SignedInUser) if err != nil { return response.Error(http.StatusInternalServerError, fmt.Sprintf("failed to get namespace: %s", namespace), err) } @@ -175,7 +175,7 @@ func (srv RulerSrv) RouteGetRulesConfig(c *models.ReqContext) response.Response func (srv RulerSrv) RoutePostNameRulesConfig(c *models.ReqContext, ruleGroupConfig apimodels.PostableRuleGroupConfig) response.Response { namespace := c.Params(":Namespace") - namespaceUID, err := srv.store.GetNamespaceUIDBySlug(namespace, c.SignedInUser.OrgId, c.SignedInUser) + namespaceUID, err := srv.store.GetNamespaceUIDByTitle(namespace, c.SignedInUser.OrgId, c.SignedInUser) if err != nil { return response.Error(http.StatusInternalServerError, fmt.Sprintf("failed to get namespace: %s", namespace), err) } diff --git a/pkg/services/ngalert/api/test-data/ruler-grafana-recipient.http b/pkg/services/ngalert/api/test-data/ruler-grafana-recipient.http index 412a7bad270..3de6a29458f 100644 --- a/pkg/services/ngalert/api/test-data/ruler-grafana-recipient.http +++ b/pkg/services/ngalert/api/test-data/ruler-grafana-recipient.http @@ -1,7 +1,7 @@ @grafanaRecipient = grafana // should point to an existing folder named alerting -@namespace1 = alerting +@namespace1 = foo%20bar // create group42 under unknown namespace - it should fail POST http://admin:admin@localhost:3000/api/ruler/{{grafanaRecipient}}/api/v1/rules/unknown diff --git a/pkg/services/ngalert/store/alert_rule.go b/pkg/services/ngalert/store/alert_rule.go index 13043a83cfa..890fa878402 100644 --- a/pkg/services/ngalert/store/alert_rule.go +++ b/pkg/services/ngalert/store/alert_rule.go @@ -43,7 +43,7 @@ type RuleStore interface { GetOrgAlertRules(query *ngmodels.ListAlertRulesQuery) error GetNamespaceAlertRules(query *ngmodels.ListNamespaceAlertRulesQuery) error GetRuleGroupAlertRules(query *ngmodels.ListRuleGroupAlertRulesQuery) error - GetNamespaceUIDBySlug(string, int64, *models.SignedInUser) (string, error) + GetNamespaceUIDByTitle(string, int64, *models.SignedInUser) (string, error) GetNamespaceByUID(string, int64, *models.SignedInUser) (string, error) GetOrgRuleGroups(query *ngmodels.ListOrgRuleGroupsQuery) error UpsertAlertRules([]UpsertRule) error @@ -310,10 +310,10 @@ func (st DBstore) GetRuleGroupAlertRules(query *ngmodels.ListRuleGroupAlertRules }) } -// GetNamespaceUIDBySlug is a handler for retrieving namespace UID by its name. -func (st DBstore) GetNamespaceUIDBySlug(namespace string, orgID int64, user *models.SignedInUser) (string, error) { +// GetNamespaceUIDByTitle is a handler for retrieving a namespace UID by its title. +func (st DBstore) GetNamespaceUIDByTitle(namespace string, orgID int64, user *models.SignedInUser) (string, error) { s := dashboards.NewFolderService(orgID, user, st.SQLStore) - folder, err := s.GetFolderBySlug(namespace) + folder, err := s.GetFolderByTitle(namespace) if err != nil { return "", err } diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index 5ccf554e599..d9ec5657938 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -202,6 +202,32 @@ func (ss *SQLStore) GetDashboard(id, orgID int64, uid, slug string) (*models.Das return &dashboard, nil } +// GetDashboardByTitle gets a dashboard by its title. +func (ss *SQLStore) GetFolderByTitle(orgID int64, title string) (*models.Dashboard, error) { + if title == "" { + return nil, models.ErrDashboardIdentifierNotSet + } + + // there is a unique constraint on org_id, folder_id, title + // there are no nested folders so the parent folder id is always 0 + dashboard := models.Dashboard{OrgId: orgID, FolderId: 0, Title: title} + has, err := ss.engine.Get(&dashboard) + if err != nil { + return nil, err + } else if !has { + return nil, models.ErrDashboardNotFound + } + + // if there is a dashboard instead of a folder with that title + if !dashboard.IsFolder { + return nil, models.ErrDashboardNotFound + } + + dashboard.SetId(dashboard.Id) + dashboard.SetUid(dashboard.Uid) + return &dashboard, nil +} + // TODO: Remove me func GetDashboard(query *models.GetDashboardQuery) error { if query.Id == 0 && len(query.Slug) == 0 && len(query.Uid) == 0 {