2022-02-16 21:15:44 +08:00
package database
import (
"context"
2022-02-23 18:12:37 +08:00
"errors"
2022-02-16 21:15:44 +08:00
"fmt"
"time"
2022-05-19 22:59:12 +08:00
"xorm.io/xorm"
2022-10-19 21:02:15 +08:00
"github.com/grafana/grafana/pkg/infra/db"
2022-02-16 21:15:44 +08:00
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
2022-03-22 21:36:50 +08:00
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
2023-01-23 21:19:25 +08:00
alertmodels "github.com/grafana/grafana/pkg/services/alerting/models"
2022-03-22 21:36:50 +08:00
"github.com/grafana/grafana/pkg/services/dashboards"
2022-06-08 18:22:55 +08:00
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
2022-08-01 23:56:36 +08:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2022-11-15 03:08:10 +08:00
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/sqlstore"
2022-02-16 21:15:44 +08:00
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
2022-05-23 23:14:27 +08:00
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
2023-01-10 22:56:33 +08:00
"github.com/grafana/grafana/pkg/services/star"
2022-08-01 23:56:36 +08:00
"github.com/grafana/grafana/pkg/services/store"
2022-09-21 20:04:01 +08:00
"github.com/grafana/grafana/pkg/services/tag"
2022-10-15 03:33:06 +08:00
"github.com/grafana/grafana/pkg/setting"
2022-02-16 21:15:44 +08:00
"github.com/grafana/grafana/pkg/util"
)
2023-03-01 23:52:16 +08:00
type dashboardStore struct {
2022-10-19 21:02:15 +08:00
store db . DB
2022-10-15 03:33:06 +08:00
cfg * setting . Cfg
2022-09-21 20:04:01 +08:00
log log . Logger
features featuremgmt . FeatureToggles
tagService tag . Service
2022-02-16 21:15:44 +08:00
}
2023-03-01 23:52:16 +08:00
// SQL bean helper to save tags
type dashboardTag struct {
2022-10-19 21:02:15 +08:00
Id int64
DashboardId int64
Term string
}
2022-05-18 02:52:22 +08:00
// DashboardStore implements the Store interface
2023-03-01 23:52:16 +08:00
var _ dashboards . Store = ( * dashboardStore ) ( nil )
2022-05-18 02:52:22 +08:00
2023-03-01 23:52:16 +08:00
func ProvideDashboardStore ( sqlStore db . DB , cfg * setting . Cfg , features featuremgmt . FeatureToggles , tagService tag . Service , quotaService quota . Service ) ( dashboards . Store , error ) {
s := & dashboardStore { store : sqlStore , cfg : cfg , log : log . New ( "dashboard-store" ) , features : features , tagService : tagService }
2022-11-15 03:08:10 +08:00
defaultLimits , err := readQuotaConfig ( cfg )
if err != nil {
return nil , err
}
if err := quotaService . RegisterQuotaReporter ( & quota . NewUsageReporter {
TargetSrv : dashboards . QuotaTargetSrv ,
DefaultLimits : defaultLimits ,
Reporter : s . Count ,
} ) ; err != nil {
return nil , err
}
return s , nil
2022-08-01 23:56:36 +08:00
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) emitEntityEvent ( ) bool {
2022-08-01 23:56:36 +08:00
return d . features != nil && d . features . IsEnabled ( featuremgmt . FlagPanelTitleSearch )
2022-02-16 21:15:44 +08:00
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) ValidateDashboardBeforeSave ( ctx context . Context , dashboard * dashboards . Dashboard , overwrite bool ) ( bool , error ) {
2022-02-16 21:15:44 +08:00
isParentFolderChanged := false
2022-10-19 21:02:15 +08:00
err := d . store . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-02-16 21:15:44 +08:00
var err error
2023-01-16 23:33:55 +08:00
isParentFolderChanged , err = getExistingDashboardByIDOrUIDForUpdate ( sess , dashboard , d . store . GetDialect ( ) , overwrite )
2022-02-16 21:15:44 +08:00
if err != nil {
return err
}
2022-10-19 21:02:15 +08:00
isParentFolderChanged , err = getExistingDashboardByTitleAndFolder ( sess , dashboard , d . store . GetDialect ( ) , overwrite ,
2022-02-16 21:15:44 +08:00
isParentFolderChanged )
if err != nil {
return err
}
return nil
} )
if err != nil {
return false , err
}
return isParentFolderChanged , nil
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) GetProvisionedDataByDashboardID ( ctx context . Context , dashboardID int64 ) ( * dashboards . DashboardProvisioning , error ) {
2023-01-18 20:52:41 +08:00
var data dashboards . DashboardProvisioning
2022-10-19 21:02:15 +08:00
err := d . store . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-02-16 21:15:44 +08:00
_ , err := sess . Where ( "dashboard_id = ?" , dashboardID ) . Get ( & data )
return err
} )
2023-01-18 20:52:41 +08:00
if data . DashboardID == 0 {
2022-02-16 21:15:44 +08:00
return nil , nil
}
return & data , err
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) GetProvisionedDataByDashboardUID ( ctx context . Context , orgID int64 , dashboardUID string ) ( * dashboards . DashboardProvisioning , error ) {
2023-01-18 20:52:41 +08:00
var provisionedDashboard dashboards . DashboardProvisioning
2022-10-19 21:02:15 +08:00
err := d . store . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2023-01-18 20:52:41 +08:00
var dashboard dashboards . Dashboard
2022-02-16 21:15:44 +08:00
exists , err := sess . Where ( "org_id = ? AND uid = ?" , orgID , dashboardUID ) . Get ( & dashboard )
if err != nil {
return err
}
if ! exists {
2022-06-30 21:31:54 +08:00
return dashboards . ErrDashboardNotFound
2022-02-16 21:15:44 +08:00
}
2023-01-18 20:52:41 +08:00
exists , err = sess . Where ( "dashboard_id = ?" , dashboard . ID ) . Get ( & provisionedDashboard )
2022-02-16 21:15:44 +08:00
if err != nil {
return err
}
if ! exists {
2022-06-30 21:31:54 +08:00
return dashboards . ErrProvisionedDashboardNotFound
2022-02-16 21:15:44 +08:00
}
return nil
} )
return & provisionedDashboard , err
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) GetProvisionedDashboardData ( ctx context . Context , name string ) ( [ ] * dashboards . DashboardProvisioning , error ) {
2023-01-18 20:52:41 +08:00
var result [ ] * dashboards . DashboardProvisioning
2022-10-19 21:02:15 +08:00
err := d . store . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-02-16 21:15:44 +08:00
return sess . Where ( "name = ?" , name ) . Find ( & result )
} )
return result , err
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) SaveProvisionedDashboard ( ctx context . Context , cmd dashboards . SaveDashboardCommand , provisioning * dashboards . DashboardProvisioning ) ( * dashboards . Dashboard , error ) {
2023-01-25 17:36:26 +08:00
var result * dashboards . Dashboard
var err error
err = d . store . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
result , err = saveDashboard ( sess , & cmd , d . emitEntityEvent ( ) )
if err != nil {
2022-02-16 21:15:44 +08:00
return err
}
if provisioning . Updated == 0 {
2023-01-25 17:36:26 +08:00
provisioning . Updated = result . Updated . Unix ( )
2022-02-16 21:15:44 +08:00
}
2023-01-25 17:36:26 +08:00
return saveProvisionedData ( sess , provisioning , result )
2022-02-16 21:15:44 +08:00
} )
2023-01-25 17:36:26 +08:00
return result , err
2022-02-16 21:15:44 +08:00
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) SaveDashboard ( ctx context . Context , cmd dashboards . SaveDashboardCommand ) ( * dashboards . Dashboard , error ) {
2023-01-25 17:36:26 +08:00
var result * dashboards . Dashboard
var err error
err = d . store . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
result , err = saveDashboard ( sess , & cmd , d . emitEntityEvent ( ) )
if err != nil {
return err
}
return nil
2022-02-16 21:15:44 +08:00
} )
2023-01-25 17:36:26 +08:00
if err != nil {
return nil , err
}
return result , err
2022-02-16 21:15:44 +08:00
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) UpdateDashboardACL ( ctx context . Context , dashboardID int64 , items [ ] * dashboards . DashboardACL ) error {
2022-10-19 21:02:15 +08:00
return d . store . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-02-16 21:15:44 +08:00
// delete existing items
_ , err := sess . Exec ( "DELETE FROM dashboard_acl WHERE dashboard_id=?" , dashboardID )
if err != nil {
return fmt . Errorf ( "deleting from dashboard_acl failed: %w" , err )
}
for _ , item := range items {
if item . UserID == 0 && item . TeamID == 0 && ( item . Role == nil || ! item . Role . IsValid ( ) ) {
2023-01-26 21:46:30 +08:00
return dashboards . ErrDashboardACLInfoMissing
2022-02-16 21:15:44 +08:00
}
if item . DashboardID == 0 {
2023-01-26 21:46:30 +08:00
return dashboards . ErrDashboardPermissionDashboardEmpty
2022-02-16 21:15:44 +08:00
}
sess . Nullable ( "user_id" , "team_id" )
if _ , err := sess . Insert ( item ) ; err != nil {
return err
}
}
2022-07-18 21:14:58 +08:00
// Update dashboard HasACL flag
2023-01-18 20:52:41 +08:00
dashboard := dashboards . Dashboard { HasACL : true }
2022-02-16 21:15:44 +08:00
_ , err = sess . Cols ( "has_acl" ) . Where ( "id=?" , dashboardID ) . Update ( & dashboard )
return err
} )
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) SaveAlerts ( ctx context . Context , dashID int64 , alerts [ ] * alertmodels . Alert ) error {
2022-10-19 21:02:15 +08:00
return d . store . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-02-16 21:15:44 +08:00
existingAlerts , err := GetAlertsByDashboardId2 ( dashID , sess )
if err != nil {
return err
}
2022-09-21 20:04:01 +08:00
if err := d . updateAlerts ( ctx , existingAlerts , alerts , d . log ) ; err != nil {
2022-02-16 21:15:44 +08:00
return err
}
2022-03-22 21:36:50 +08:00
if err := d . deleteMissingAlerts ( existingAlerts , alerts , sess ) ; err != nil {
2022-02-16 21:15:44 +08:00
return err
}
return nil
} )
}
// UnprovisionDashboard removes row in dashboard_provisioning for the dashboard making it seem as if manually created.
// The dashboard will still have `created_by = -1` to see it was not created by any particular user.
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) UnprovisionDashboard ( ctx context . Context , id int64 ) error {
2022-10-19 21:02:15 +08:00
return d . store . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2023-01-18 20:52:41 +08:00
_ , err := sess . Where ( "dashboard_id = ?" , id ) . Delete ( & dashboards . DashboardProvisioning { } )
2022-02-16 21:15:44 +08:00
return err
} )
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) DeleteOrphanedProvisionedDashboards ( ctx context . Context , cmd * dashboards . DeleteOrphanedProvisionedDashboardsCommand ) error {
2022-10-19 21:02:15 +08:00
return d . store . WithDbSession ( ctx , func ( sess * db . Session ) error {
2023-01-18 20:52:41 +08:00
var result [ ] * dashboards . DashboardProvisioning
2022-02-23 18:12:37 +08:00
2023-08-30 23:46:47 +08:00
convertedReaderNames := make ( [ ] any , len ( cmd . ReaderNames ) )
2022-02-23 18:12:37 +08:00
for index , readerName := range cmd . ReaderNames {
convertedReaderNames [ index ] = readerName
}
err := sess . NotIn ( "name" , convertedReaderNames ... ) . Find ( & result )
if err != nil {
return err
}
for _ , deleteDashCommand := range result {
2023-01-18 20:52:41 +08:00
err := d . DeleteDashboard ( ctx , & dashboards . DeleteDashboardCommand { ID : deleteDashCommand . DashboardID } )
2022-06-30 21:31:54 +08:00
if err != nil && ! errors . Is ( err , dashboards . ErrDashboardNotFound ) {
2022-02-23 18:12:37 +08:00
return err
}
}
return nil
} )
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) Count ( ctx context . Context , scopeParams * quota . ScopeParameters ) ( * quota . Map , error ) {
2022-11-15 03:08:10 +08:00
u := & quota . Map { }
type result struct {
Count int64
}
r := result { }
if err := d . store . WithDbSession ( ctx , func ( sess * sqlstore . DBSession ) error {
rawSQL := fmt . Sprintf ( "SELECT COUNT(*) AS count FROM dashboard WHERE is_folder=%s" , d . store . GetDialect ( ) . BooleanStr ( false ) )
if _ , err := sess . SQL ( rawSQL ) . Get ( & r ) ; err != nil {
return err
}
return nil
} ) ; err != nil {
return u , err
} else {
tag , err := quota . NewTag ( dashboards . QuotaTargetSrv , dashboards . QuotaTarget , quota . GlobalScope )
if err != nil {
return nil , err
}
u . Set ( tag , r . Count )
}
2023-01-16 18:54:15 +08:00
if scopeParams != nil && scopeParams . OrgID != 0 {
2022-11-15 03:08:10 +08:00
if err := d . store . WithDbSession ( ctx , func ( sess * sqlstore . DBSession ) error {
rawSQL := fmt . Sprintf ( "SELECT COUNT(*) AS count FROM dashboard WHERE org_id=? AND is_folder=%s" , d . store . GetDialect ( ) . BooleanStr ( false ) )
if _ , err := sess . SQL ( rawSQL , scopeParams . OrgID ) . Get ( & r ) ; err != nil {
return err
}
return nil
} ) ; err != nil {
return u , err
} else {
tag , err := quota . NewTag ( dashboards . QuotaTargetSrv , dashboards . QuotaTarget , quota . OrgScope )
if err != nil {
return nil , err
}
u . Set ( tag , r . Count )
}
}
return u , nil
}
2023-01-16 23:33:55 +08:00
func getExistingDashboardByIDOrUIDForUpdate ( sess * db . Session , dash * dashboards . Dashboard , dialect migrator . Dialect , overwrite bool ) ( bool , error ) {
2022-02-16 21:15:44 +08:00
dashWithIdExists := false
isParentFolderChanged := false
2023-01-18 20:52:41 +08:00
var existingById dashboards . Dashboard
2022-02-16 21:15:44 +08:00
2023-01-16 23:33:55 +08:00
if dash . ID > 0 {
2022-02-16 21:15:44 +08:00
var err error
2023-01-16 23:33:55 +08:00
dashWithIdExists , err = sess . Where ( "id=? AND org_id=?" , dash . ID , dash . OrgID ) . Get ( & existingById )
2022-02-16 21:15:44 +08:00
if err != nil {
return false , fmt . Errorf ( "SQL query for existing dashboard by ID failed: %w" , err )
}
if ! dashWithIdExists {
2022-06-30 21:31:54 +08:00
return false , dashboards . ErrDashboardNotFound
2022-02-16 21:15:44 +08:00
}
2023-01-16 23:33:55 +08:00
if dash . UID == "" {
2023-01-18 20:52:41 +08:00
dash . SetUID ( existingById . UID )
2022-02-16 21:15:44 +08:00
}
}
dashWithUidExists := false
2023-01-18 20:52:41 +08:00
var existingByUid dashboards . Dashboard
2022-02-16 21:15:44 +08:00
2023-01-16 23:33:55 +08:00
if dash . UID != "" {
2022-02-16 21:15:44 +08:00
var err error
2023-01-16 23:33:55 +08:00
dashWithUidExists , err = sess . Where ( "org_id=? AND uid=?" , dash . OrgID , dash . UID ) . Get ( & existingByUid )
2022-02-16 21:15:44 +08:00
if err != nil {
return false , fmt . Errorf ( "SQL query for existing dashboard by UID failed: %w" , err )
}
}
2023-01-16 23:33:55 +08:00
if dash . FolderID > 0 {
2023-01-18 20:52:41 +08:00
var existingFolder dashboards . Dashboard
2023-01-16 23:33:55 +08:00
folderExists , err := sess . Where ( "org_id=? AND id=? AND is_folder=?" , dash . OrgID , dash . FolderID ,
2022-02-16 21:15:44 +08:00
dialect . BooleanStr ( true ) ) . Get ( & existingFolder )
if err != nil {
return false , fmt . Errorf ( "SQL query for folder failed: %w" , err )
}
if ! folderExists {
2022-06-30 21:31:54 +08:00
return false , dashboards . ErrDashboardFolderNotFound
2022-02-16 21:15:44 +08:00
}
}
if ! dashWithIdExists && ! dashWithUidExists {
return false , nil
}
2023-01-18 20:52:41 +08:00
if dashWithIdExists && dashWithUidExists && existingById . ID != existingByUid . ID {
2022-06-30 21:31:54 +08:00
return false , dashboards . ErrDashboardWithSameUIDExists
2022-02-16 21:15:44 +08:00
}
existing := existingById
if ! dashWithIdExists && dashWithUidExists {
2023-01-18 20:52:41 +08:00
dash . SetID ( existingByUid . ID )
dash . SetUID ( existingByUid . UID )
2022-02-16 21:15:44 +08:00
existing = existingByUid
}
if ( existing . IsFolder && ! dash . IsFolder ) ||
( ! existing . IsFolder && dash . IsFolder ) {
2022-06-30 21:31:54 +08:00
return isParentFolderChanged , dashboards . ErrDashboardTypeMismatch
2022-02-16 21:15:44 +08:00
}
2023-01-18 20:52:41 +08:00
if ! dash . IsFolder && dash . FolderID != existing . FolderID {
2022-02-16 21:15:44 +08:00
isParentFolderChanged = true
}
// check for is someone else has written in between
if dash . Version != existing . Version {
if overwrite {
dash . SetVersion ( existing . Version )
} else {
2022-06-30 21:31:54 +08:00
return isParentFolderChanged , dashboards . ErrDashboardVersionMismatch
2022-02-16 21:15:44 +08:00
}
}
// do not allow plugin dashboard updates without overwrite flag
2023-01-18 20:52:41 +08:00
if existing . PluginID != "" && ! overwrite {
return isParentFolderChanged , dashboards . UpdatePluginDashboardError { PluginId : existing . PluginID }
2022-02-16 21:15:44 +08:00
}
return isParentFolderChanged , nil
}
2023-06-30 04:15:38 +08:00
// getExistingDashboardByTitleAndFolder returns a boolean (on whether the parent folder changed) and an error for if the dashboard already exists.
2023-01-16 23:33:55 +08:00
func getExistingDashboardByTitleAndFolder ( sess * db . Session , dash * dashboards . Dashboard , dialect migrator . Dialect , overwrite ,
2022-02-16 21:15:44 +08:00
isParentFolderChanged bool ) ( bool , error ) {
2023-01-18 20:52:41 +08:00
var existing dashboards . Dashboard
2023-06-30 04:15:38 +08:00
exists , err := sess . Where ( "org_id=? AND title=? AND (is_folder=? OR folder_id=?)" , dash . OrgID , dash . Title ,
2023-01-16 23:33:55 +08:00
dialect . BooleanStr ( true ) , dash . FolderID ) . Get ( & existing )
2022-02-16 21:15:44 +08:00
if err != nil {
return isParentFolderChanged , fmt . Errorf ( "SQL query for existing dashboard by org ID or folder ID failed: %w" , err )
}
2023-01-18 20:52:41 +08:00
if exists && dash . ID != existing . ID {
2022-02-16 21:15:44 +08:00
if existing . IsFolder && ! dash . IsFolder {
2022-06-30 21:31:54 +08:00
return isParentFolderChanged , dashboards . ErrDashboardWithSameNameAsFolder
2022-02-16 21:15:44 +08:00
}
if ! existing . IsFolder && dash . IsFolder {
2022-06-30 21:31:54 +08:00
return isParentFolderChanged , dashboards . ErrDashboardFolderWithSameNameAsDashboard
2022-02-16 21:15:44 +08:00
}
2023-01-18 20:52:41 +08:00
if ! dash . IsFolder && ( dash . FolderID != existing . FolderID || dash . ID == 0 ) {
2022-02-16 21:15:44 +08:00
isParentFolderChanged = true
}
if overwrite {
2023-01-18 20:52:41 +08:00
dash . SetID ( existing . ID )
dash . SetUID ( existing . UID )
2022-02-16 21:15:44 +08:00
dash . SetVersion ( existing . Version )
} else {
2022-06-30 21:31:54 +08:00
return isParentFolderChanged , dashboards . ErrDashboardWithSameNameInFolderExists
2022-02-16 21:15:44 +08:00
}
}
return isParentFolderChanged , nil
}
2023-01-25 17:36:26 +08:00
func saveDashboard ( sess * db . Session , cmd * dashboards . SaveDashboardCommand , emitEntityEvent bool ) ( * dashboards . Dashboard , error ) {
2022-02-16 21:15:44 +08:00
dash := cmd . GetDashboardModel ( )
2023-01-16 23:33:55 +08:00
userId := cmd . UserID
2022-02-16 21:15:44 +08:00
if userId == 0 {
userId = - 1
}
2023-01-16 23:33:55 +08:00
if dash . ID > 0 {
2023-01-18 20:52:41 +08:00
var existing dashboards . Dashboard
2023-01-16 23:33:55 +08:00
dashWithIdExists , err := sess . Where ( "id=? AND org_id=?" , dash . ID , dash . OrgID ) . Get ( & existing )
2022-02-16 21:15:44 +08:00
if err != nil {
2023-01-25 17:36:26 +08:00
return nil , err
2022-02-16 21:15:44 +08:00
}
if ! dashWithIdExists {
2023-01-25 17:36:26 +08:00
return nil , dashboards . ErrDashboardNotFound
2022-02-16 21:15:44 +08:00
}
// check for is someone else has written in between
if dash . Version != existing . Version {
if cmd . Overwrite {
dash . SetVersion ( existing . Version )
} else {
2023-01-25 17:36:26 +08:00
return nil , dashboards . ErrDashboardVersionMismatch
2022-02-16 21:15:44 +08:00
}
}
// do not allow plugin dashboard updates without overwrite flag
2023-01-18 20:52:41 +08:00
if existing . PluginID != "" && ! cmd . Overwrite {
2023-01-25 17:36:26 +08:00
return nil , dashboards . UpdatePluginDashboardError { PluginId : existing . PluginID }
2022-02-16 21:15:44 +08:00
}
}
2023-01-16 23:33:55 +08:00
if dash . UID == "" {
2023-02-07 09:44:37 +08:00
dash . SetUID ( util . GenerateShortUID ( ) )
2022-02-16 21:15:44 +08:00
}
parentVersion := dash . Version
var affectedRows int64
var err error
2023-01-16 23:33:55 +08:00
if dash . ID == 0 {
2022-02-16 21:15:44 +08:00
dash . SetVersion ( 1 )
dash . Created = time . Now ( )
dash . CreatedBy = userId
dash . Updated = time . Now ( )
dash . UpdatedBy = userId
metrics . MApiDashboardInsert . Inc ( )
2023-10-24 15:04:45 +08:00
affectedRows , err = sess . Nullable ( "folder_uid" ) . Insert ( dash )
2022-02-16 21:15:44 +08:00
} else {
dash . SetVersion ( dash . Version + 1 )
if ! cmd . UpdatedAt . IsZero ( ) {
dash . Updated = cmd . UpdatedAt
} else {
dash . Updated = time . Now ( )
}
dash . UpdatedBy = userId
2023-10-24 15:04:45 +08:00
affectedRows , err = sess . MustCols ( "folder_id" , "folder_uid" ) . Nullable ( "folder_uid" ) . ID ( dash . ID ) . Update ( dash )
2022-02-16 21:15:44 +08:00
}
if err != nil {
2023-01-25 17:36:26 +08:00
return nil , err
2022-02-16 21:15:44 +08:00
}
if affectedRows == 0 {
2023-01-25 17:36:26 +08:00
return nil , dashboards . ErrDashboardNotFound
2022-02-16 21:15:44 +08:00
}
2022-06-08 18:22:55 +08:00
dashVersion := & dashver . DashboardVersion {
2023-01-16 23:33:55 +08:00
DashboardID : dash . ID ,
2022-02-16 21:15:44 +08:00
ParentVersion : parentVersion ,
RestoredFrom : cmd . RestoredFrom ,
Version : dash . Version ,
Created : time . Now ( ) ,
CreatedBy : dash . UpdatedBy ,
Message : cmd . Message ,
Data : dash . Data ,
}
// insert version entry
if affectedRows , err = sess . Insert ( dashVersion ) ; err != nil {
2023-01-25 17:36:26 +08:00
return nil , err
2022-02-16 21:15:44 +08:00
} else if affectedRows == 0 {
2023-01-25 17:36:26 +08:00
return nil , dashboards . ErrDashboardNotFound
2022-02-16 21:15:44 +08:00
}
// delete existing tags
2023-01-16 23:33:55 +08:00
if _ , err = sess . Exec ( "DELETE FROM dashboard_tag WHERE dashboard_id=?" , dash . ID ) ; err != nil {
2023-01-25 17:36:26 +08:00
return nil , err
2022-02-16 21:15:44 +08:00
}
// insert new tags
tags := dash . GetTags ( )
if len ( tags ) > 0 {
for _ , tag := range tags {
2023-03-01 23:52:16 +08:00
if _ , err := sess . Insert ( dashboardTag { DashboardId : dash . ID , Term : tag } ) ; err != nil {
2023-01-25 17:36:26 +08:00
return nil , err
2022-02-16 21:15:44 +08:00
}
}
}
2022-08-01 23:56:36 +08:00
if emitEntityEvent {
_ , err := sess . Insert ( createEntityEvent ( dash , store . EntityEventTypeUpdate ) )
if err != nil {
2023-01-25 17:36:26 +08:00
return dash , err
2022-08-01 23:56:36 +08:00
}
}
2023-01-25 17:36:26 +08:00
return dash , nil
2022-02-16 21:15:44 +08:00
}
2023-01-16 23:33:55 +08:00
func saveProvisionedData ( sess * db . Session , provisioning * dashboards . DashboardProvisioning , dashboard * dashboards . Dashboard ) error {
2023-01-18 20:52:41 +08:00
result := & dashboards . DashboardProvisioning { }
2022-02-16 21:15:44 +08:00
2023-01-16 23:33:55 +08:00
exist , err := sess . Where ( "dashboard_id=? AND name = ?" , dashboard . ID , provisioning . Name ) . Get ( result )
2022-02-16 21:15:44 +08:00
if err != nil {
return err
}
2023-01-18 20:52:41 +08:00
provisioning . ID = result . ID
2023-01-16 23:33:55 +08:00
provisioning . DashboardID = dashboard . ID
2022-02-16 21:15:44 +08:00
if exist {
2023-01-18 20:52:41 +08:00
_ , err = sess . ID ( result . ID ) . Update ( provisioning )
2022-02-16 21:15:44 +08:00
} else {
_ , err = sess . Insert ( provisioning )
}
return err
}
2023-01-23 21:19:25 +08:00
func GetAlertsByDashboardId2 ( dashboardId int64 , sess * db . Session ) ( [ ] * alertmodels . Alert , error ) {
alerts := make ( [ ] * alertmodels . Alert , 0 )
2022-02-16 21:15:44 +08:00
err := sess . Where ( "dashboard_id = ?" , dashboardId ) . Find ( & alerts )
if err != nil {
2023-01-23 21:19:25 +08:00
return [ ] * alertmodels . Alert { } , err
2022-02-16 21:15:44 +08:00
}
return alerts , nil
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) updateAlerts ( ctx context . Context , existingAlerts [ ] * alertmodels . Alert , alertsIn [ ] * alertmodels . Alert , log log . Logger ) error {
2022-10-19 21:02:15 +08:00
return d . store . WithDbSession ( ctx , func ( sess * db . Session ) error {
2023-01-23 21:19:25 +08:00
for _ , alert := range alertsIn {
2022-09-21 20:04:01 +08:00
update := false
2023-01-23 21:19:25 +08:00
var alertToUpdate * alertmodels . Alert
2022-09-21 20:04:01 +08:00
for _ , k := range existingAlerts {
2023-02-03 00:22:43 +08:00
if alert . PanelID == k . PanelID {
2022-09-21 20:04:01 +08:00
update = true
2023-02-03 00:22:43 +08:00
alert . ID = k . ID
2022-09-21 20:04:01 +08:00
alertToUpdate = k
break
}
2022-02-16 21:15:44 +08:00
}
2022-09-21 20:04:01 +08:00
if update {
if alertToUpdate . ContainsUpdates ( alert ) {
alert . Updated = time . Now ( )
alert . State = alertToUpdate . State
sess . MustCols ( "message" , "for" )
2023-02-03 00:22:43 +08:00
_ , err := sess . ID ( alert . ID ) . Update ( alert )
2022-09-21 20:04:01 +08:00
if err != nil {
return err
}
2023-02-03 00:22:43 +08:00
log . Debug ( "Alert updated" , "name" , alert . Name , "id" , alert . ID )
2022-09-21 20:04:01 +08:00
}
} else {
2022-02-16 21:15:44 +08:00
alert . Updated = time . Now ( )
2022-09-21 20:04:01 +08:00
alert . Created = time . Now ( )
2023-01-23 21:19:25 +08:00
alert . State = alertmodels . AlertStateUnknown
2022-09-21 20:04:01 +08:00
alert . NewStateDate = time . Now ( )
2022-02-16 21:15:44 +08:00
2022-09-21 20:04:01 +08:00
_ , err := sess . Insert ( alert )
2022-02-16 21:15:44 +08:00
if err != nil {
return err
}
2023-02-03 00:22:43 +08:00
log . Debug ( "Alert inserted" , "name" , alert . Name , "id" , alert . ID )
2022-02-16 21:15:44 +08:00
}
2022-09-21 20:04:01 +08:00
tags := alert . GetTagsFromSettings ( )
2023-02-03 00:22:43 +08:00
if _ , err := sess . Exec ( "DELETE FROM alert_rule_tag WHERE alert_id = ?" , alert . ID ) ; err != nil {
2022-02-16 21:15:44 +08:00
return err
}
2022-09-21 20:04:01 +08:00
if tags != nil {
tags , err := d . tagService . EnsureTagsExist ( ctx , tags )
if err != nil {
2022-02-16 21:15:44 +08:00
return err
}
2022-09-21 20:04:01 +08:00
for _ , tag := range tags {
2023-02-03 00:22:43 +08:00
if _ , err := sess . Exec ( "INSERT INTO alert_rule_tag (alert_id, tag_id) VALUES(?,?)" , alert . ID , tag . Id ) ; err != nil {
2022-09-21 20:04:01 +08:00
return err
}
}
2022-02-16 21:15:44 +08:00
}
}
2022-09-21 20:04:01 +08:00
return nil
} )
2022-02-16 21:15:44 +08:00
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) deleteMissingAlerts ( alerts [ ] * alertmodels . Alert , existingAlerts [ ] * alertmodels . Alert , sess * db . Session ) error {
2022-02-16 21:15:44 +08:00
for _ , missingAlert := range alerts {
missing := true
for _ , k := range existingAlerts {
2023-02-03 00:22:43 +08:00
if missingAlert . PanelID == k . PanelID {
2022-02-16 21:15:44 +08:00
missing = false
break
}
}
if missing {
2023-02-03 00:22:43 +08:00
if err := d . deleteAlertByIdInternal ( missingAlert . ID , "Removed from dashboard" , sess ) ; err != nil {
2022-02-16 21:15:44 +08:00
// No use trying to delete more, since we're in a transaction and it will be
// rolled back on error.
return err
}
}
}
return nil
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) deleteAlertByIdInternal ( alertId int64 , reason string , sess * db . Session ) error {
2022-03-22 21:36:50 +08:00
d . log . Debug ( "Deleting alert" , "id" , alertId , "reason" , reason )
2022-02-16 21:15:44 +08:00
if _ , err := sess . Exec ( "DELETE FROM alert WHERE id = ?" , alertId ) ; err != nil {
return err
}
if _ , err := sess . Exec ( "DELETE FROM annotation WHERE alert_id = ?" , alertId ) ; err != nil {
return err
}
if _ , err := sess . Exec ( "DELETE FROM alert_notification_state WHERE alert_id = ?" , alertId ) ; err != nil {
return err
}
if _ , err := sess . Exec ( "DELETE FROM alert_rule_tag WHERE alert_id = ?" , alertId ) ; err != nil {
return err
}
return nil
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) GetDashboardsByPluginID ( ctx context . Context , query * dashboards . GetDashboardsByPluginIDQuery ) ( [ ] * dashboards . Dashboard , error ) {
2023-01-25 17:36:26 +08:00
var dashboards = make ( [ ] * dashboards . Dashboard , 0 )
err := d . store . WithDbSession ( ctx , func ( dbSession * db . Session ) error {
2022-10-19 21:02:15 +08:00
whereExpr := "org_id=? AND plugin_id=? AND is_folder=" + d . store . GetDialect ( ) . BooleanStr ( false )
2022-03-11 01:38:04 +08:00
2023-01-18 20:52:41 +08:00
err := dbSession . Where ( whereExpr , query . OrgID , query . PluginID ) . Find ( & dashboards )
2022-03-11 01:38:04 +08:00
return err
} )
2023-01-25 17:36:26 +08:00
if err != nil {
return nil , err
}
return dashboards , nil
2022-03-11 01:38:04 +08:00
}
2022-03-22 21:36:50 +08:00
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) DeleteDashboard ( ctx context . Context , cmd * dashboards . DeleteDashboardCommand ) error {
2022-10-19 21:02:15 +08:00
return d . store . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
2022-08-01 23:56:36 +08:00
return d . deleteDashboard ( cmd , sess , d . emitEntityEvent ( ) )
2022-03-22 21:36:50 +08:00
} )
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) deleteDashboard ( cmd * dashboards . DeleteDashboardCommand , sess * db . Session , emitEntityEvent bool ) error {
2023-01-18 20:52:41 +08:00
dashboard := dashboards . Dashboard { ID : cmd . ID , OrgID : cmd . OrgID }
2022-03-22 21:36:50 +08:00
has , err := sess . Get ( & dashboard )
if err != nil {
return err
} else if ! has {
2022-06-30 21:31:54 +08:00
return dashboards . ErrDashboardNotFound
2022-03-22 21:36:50 +08:00
}
deletes := [ ] string {
"DELETE FROM dashboard_tag WHERE dashboard_id = ? " ,
"DELETE FROM star WHERE dashboard_id = ? " ,
"DELETE FROM dashboard WHERE id = ?" ,
"DELETE FROM playlist_item WHERE type = 'dashboard_by_id' AND value = ?" ,
"DELETE FROM dashboard_version WHERE dashboard_id = ?" ,
"DELETE FROM dashboard_provisioning WHERE dashboard_id = ?" ,
"DELETE FROM dashboard_acl WHERE dashboard_id = ?" ,
}
if dashboard . IsFolder {
deletes = append ( deletes , "DELETE FROM dashboard WHERE folder_id = ?" )
2023-07-10 17:12:04 +08:00
if err := d . deleteChildrenDashboardAssociations ( sess , & dashboard ) ; err != nil {
2022-03-22 21:36:50 +08:00
return err
}
// remove all access control permission with folder scope
2023-07-10 17:12:04 +08:00
err := d . deleteResourcePermissions ( sess , dashboard . OrgID , dashboards . ScopeFoldersProvider . GetResourceScopeUID ( dashboard . UID ) )
2022-03-22 21:36:50 +08:00
if err != nil {
return err
}
2023-04-14 17:17:23 +08:00
if err := deleteFolderAlertRules ( sess , dashboard , cmd . ForceDeleteFolderRules ) ; err != nil {
2022-03-22 21:36:50 +08:00
return err
}
} else {
2023-07-10 17:12:04 +08:00
if err := d . deleteResourcePermissions ( sess , dashboard . OrgID , ac . GetResourceScopeUID ( "dashboards" , dashboard . UID ) ) ; err != nil {
2022-03-22 21:36:50 +08:00
return err
}
}
2023-01-16 23:33:55 +08:00
if err := d . deleteAlertDefinition ( dashboard . ID , sess ) ; err != nil {
2022-03-22 21:36:50 +08:00
return err
}
2023-06-01 20:31:03 +08:00
_ , err = sess . Exec ( "DELETE FROM annotation WHERE dashboard_id = ? AND org_id = ?" , dashboard . ID , dashboard . OrgID )
if err != nil {
return err
}
2022-03-22 21:36:50 +08:00
for _ , sql := range deletes {
2023-01-16 23:33:55 +08:00
_ , err := sess . Exec ( sql , dashboard . ID )
2022-03-22 21:36:50 +08:00
if err != nil {
return err
}
}
2022-08-01 23:56:36 +08:00
if emitEntityEvent {
_ , err := sess . Insert ( createEntityEvent ( & dashboard , store . EntityEventTypeDelete ) )
if err != nil {
return err
}
}
2022-03-22 21:36:50 +08:00
return nil
}
2023-07-10 17:12:04 +08:00
// FIXME: Remove me and handle nested deletions in the service with the DashboardPermissionsService
func ( d * dashboardStore ) deleteResourcePermissions ( sess * db . Session , orgID int64 , resourceScope string ) error {
// retrieve all permissions for the resource scope and org id
var permissionIDs [ ] int64
err := sess . SQL ( "SELECT permission.id FROM permission INNER JOIN role ON permission.role_id = role.id WHERE permission.scope = ? AND role.org_id = ?" , resourceScope , orgID ) . Find ( & permissionIDs )
if err != nil {
return err
}
if len ( permissionIDs ) == 0 {
return nil
}
// delete the permissions
_ , err = sess . In ( "id" , permissionIDs ) . Delete ( & ac . Permission { } )
return err
}
func ( d * dashboardStore ) deleteChildrenDashboardAssociations ( sess * db . Session , dashboard * dashboards . Dashboard ) error {
2023-04-14 17:17:23 +08:00
var dashIds [ ] struct {
Id int64
Uid string
}
err := sess . SQL ( "SELECT id, uid FROM dashboard WHERE folder_id = ?" , dashboard . ID ) . Find ( & dashIds )
if err != nil {
return err
}
if len ( dashIds ) > 0 {
for _ , dash := range dashIds {
if err := d . deleteAlertDefinition ( dash . Id , sess ) ; err != nil {
return err
}
// remove all access control permission with child dashboard scopes
2023-07-10 17:12:04 +08:00
if err := d . deleteResourcePermissions ( sess , dashboard . OrgID , ac . GetResourceScopeUID ( "dashboards" , dash . Uid ) ) ; err != nil {
2023-04-14 17:17:23 +08:00
return err
}
}
childrenDeletes := [ ] string {
"DELETE FROM dashboard_tag WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)" ,
"DELETE FROM star WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)" ,
"DELETE FROM dashboard_version WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)" ,
"DELETE FROM dashboard_provisioning WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)" ,
"DELETE FROM dashboard_acl WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)" ,
}
2023-06-01 20:31:03 +08:00
_ , err = sess . Exec ( "DELETE FROM annotation WHERE org_id = ? AND dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)" , dashboard . OrgID , dashboard . OrgID , dashboard . ID )
if err != nil {
return err
}
2023-04-14 17:17:23 +08:00
for _ , sql := range childrenDeletes {
_ , err := sess . Exec ( sql , dashboard . OrgID , dashboard . ID )
if err != nil {
return err
}
}
}
return nil
}
func deleteFolderAlertRules ( sess * db . Session , dashboard dashboards . Dashboard , forceDeleteFolderAlertRules bool ) error {
var existingRuleID int64
exists , err := sess . Table ( "alert_rule" ) . Where ( "namespace_uid = (SELECT uid FROM dashboard WHERE id = ?)" , dashboard . ID ) . Cols ( "id" ) . Get ( & existingRuleID )
if err != nil {
return err
}
if exists {
if ! forceDeleteFolderAlertRules {
return fmt . Errorf ( "folder cannot be deleted: %w" , dashboards . ErrFolderContainsAlertRules )
}
// Delete all rules under this folder.
deleteNGAlertsByFolder := [ ] string {
"DELETE FROM alert_rule WHERE namespace_uid = (SELECT uid FROM dashboard WHERE id = ?)" ,
"DELETE FROM alert_rule_version WHERE rule_namespace_uid = (SELECT uid FROM dashboard WHERE id = ?)" ,
}
for _ , sql := range deleteNGAlertsByFolder {
_ , err := sess . Exec ( sql , dashboard . ID )
if err != nil {
return err
}
}
}
return nil
}
2023-01-16 23:33:55 +08:00
func createEntityEvent ( dashboard * dashboards . Dashboard , eventType store . EntityEventType ) * store . EntityEvent {
2022-08-01 23:56:36 +08:00
var entityEvent * store . EntityEvent
if dashboard . IsFolder {
entityEvent = & store . EntityEvent {
EventType : eventType ,
2023-01-16 23:33:55 +08:00
EntityId : store . CreateDatabaseEntityId ( dashboard . UID , dashboard . OrgID , store . EntityTypeFolder ) ,
2022-08-01 23:56:36 +08:00
Created : time . Now ( ) . Unix ( ) ,
}
} else {
entityEvent = & store . EntityEvent {
EventType : eventType ,
2023-01-16 23:33:55 +08:00
EntityId : store . CreateDatabaseEntityId ( dashboard . UID , dashboard . OrgID , store . EntityTypeDashboard ) ,
2022-08-01 23:56:36 +08:00
Created : time . Now ( ) . Unix ( ) ,
}
}
return entityEvent
}
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) deleteAlertDefinition ( dashboardId int64 , sess * db . Session ) error {
2023-01-23 21:19:25 +08:00
alerts := make ( [ ] * alertmodels . Alert , 0 )
2022-03-22 21:36:50 +08:00
if err := sess . Where ( "dashboard_id = ?" , dashboardId ) . Find ( & alerts ) ; err != nil {
return err
}
for _ , alert := range alerts {
2023-02-03 00:22:43 +08:00
if err := d . deleteAlertByIdInternal ( alert . ID , "Dashboard deleted" , sess ) ; err != nil {
2022-03-22 21:36:50 +08:00
// If we return an error, the current transaction gets rolled back, so no use
// trying to delete more
return err
}
}
return nil
}
2022-05-18 02:52:22 +08:00
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) GetDashboard ( ctx context . Context , query * dashboards . GetDashboardQuery ) ( * dashboards . Dashboard , error ) {
2023-01-25 17:36:26 +08:00
var queryResult * dashboards . Dashboard
2022-10-19 21:02:15 +08:00
err := d . store . WithDbSession ( ctx , func ( sess * db . Session ) error {
2023-11-09 23:53:39 +08:00
// nolint:staticcheck
2023-03-28 19:24:19 +08:00
if query . ID == 0 && len ( query . UID ) == 0 && ( query . Title == nil || query . FolderID == nil ) {
2022-06-30 21:31:54 +08:00
return dashboards . ErrDashboardIdentifierNotSet
2022-05-18 02:52:22 +08:00
}
2023-03-28 19:24:19 +08:00
dashboard := dashboards . Dashboard { OrgID : query . OrgID , ID : query . ID , UID : query . UID }
mustCols := [ ] string { }
if query . Title != nil {
dashboard . Title = * query . Title
mustCols = append ( mustCols , "title" )
}
2023-11-09 23:53:39 +08:00
// nolint:staticcheck
2023-03-28 19:24:19 +08:00
if query . FolderID != nil {
dashboard . FolderID = * query . FolderID
mustCols = append ( mustCols , "folder_id" )
}
has , err := sess . MustCols ( mustCols ... ) . Get ( & dashboard )
2022-05-18 02:52:22 +08:00
if err != nil {
return err
} else if ! has {
2022-06-30 21:31:54 +08:00
return dashboards . ErrDashboardNotFound
2022-05-18 02:52:22 +08:00
}
2023-01-16 23:33:55 +08:00
dashboard . SetID ( dashboard . ID )
dashboard . SetUID ( dashboard . UID )
2023-01-25 17:36:26 +08:00
queryResult = & dashboard
2022-05-18 02:52:22 +08:00
return nil
} )
2022-06-07 17:02:20 +08:00
2023-01-25 17:36:26 +08:00
return queryResult , err
2022-05-18 02:52:22 +08:00
}
2022-05-19 22:13:02 +08:00
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) GetDashboardUIDByID ( ctx context . Context , query * dashboards . GetDashboardRefByIDQuery ) ( * dashboards . DashboardRef , error ) {
2023-01-25 17:36:26 +08:00
us := & dashboards . DashboardRef { }
err := d . store . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-05-19 22:13:02 +08:00
var rawSQL = ` SELECT uid, slug from dashboard WHERE Id=? `
2023-01-16 23:33:55 +08:00
exists , err := sess . SQL ( rawSQL , query . ID ) . Get ( us )
2022-05-19 22:13:02 +08:00
if err != nil {
return err
} else if ! exists {
2022-06-30 21:31:54 +08:00
return dashboards . ErrDashboardNotFound
2022-05-19 22:13:02 +08:00
}
return nil
} )
2023-01-25 17:36:26 +08:00
if err != nil {
return nil , err
}
return us , nil
2022-05-19 22:13:02 +08:00
}
2022-05-19 22:59:12 +08:00
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) GetDashboards ( ctx context . Context , query * dashboards . GetDashboardsQuery ) ( [ ] * dashboards . Dashboard , error ) {
2023-01-25 17:36:26 +08:00
var dashboards = make ( [ ] * dashboards . Dashboard , 0 )
err := d . store . WithDbSession ( ctx , func ( sess * db . Session ) error {
2023-01-16 23:33:55 +08:00
if len ( query . DashboardIDs ) == 0 && len ( query . DashboardUIDs ) == 0 {
2023-01-10 22:56:33 +08:00
return star . ErrCommandValidationFailed
2022-05-19 22:59:12 +08:00
}
var session * xorm . Session
2023-01-16 23:33:55 +08:00
if len ( query . DashboardIDs ) > 0 {
session = sess . In ( "id" , query . DashboardIDs )
2022-05-19 22:59:12 +08:00
} else {
2023-01-16 23:33:55 +08:00
session = sess . In ( "uid" , query . DashboardUIDs )
2022-05-19 22:59:12 +08:00
}
2023-01-17 04:59:43 +08:00
if query . OrgID > 0 {
session = sess . Where ( "org_id = ?" , query . OrgID )
}
2022-05-19 22:59:12 +08:00
err := session . Find ( & dashboards )
return err
} )
2023-01-25 17:36:26 +08:00
if err != nil {
return nil , err
}
return dashboards , nil
2022-05-19 22:59:12 +08:00
}
2022-05-23 23:14:27 +08:00
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) FindDashboards ( ctx context . Context , query * dashboards . FindPersistedDashboardsQuery ) ( [ ] dashboards . DashboardSearchProjection , error ) {
2023-07-19 17:37:27 +08:00
recursiveQueriesAreSupported , err := d . store . RecursiveQueriesAreSupported ( )
if err != nil {
return nil , err
2022-05-23 23:14:27 +08:00
}
2023-08-30 23:46:47 +08:00
filters := [ ] any {
2023-07-19 17:37:27 +08:00
permissions . NewAccessControlDashboardPermissionFilter ( query . SignedInUser , query . Permission , query . Type , d . features , recursiveQueriesAreSupported ) ,
2022-05-23 23:14:27 +08:00
}
for _ , filter := range query . Sort . Filter {
filters = append ( filters , filter )
}
filters = append ( filters , query . Filters ... )
2023-08-04 17:43:47 +08:00
var orgID int64
2022-05-23 23:14:27 +08:00
if query . OrgId != 0 {
2023-08-04 17:43:47 +08:00
orgID = query . OrgId
filters = append ( filters , searchstore . OrgFilter { OrgId : orgID } )
2022-08-11 19:28:55 +08:00
} else if query . SignedInUser . OrgID != 0 {
2023-08-04 17:43:47 +08:00
orgID = query . SignedInUser . OrgID
filters = append ( filters , searchstore . OrgFilter { OrgId : orgID } )
2022-05-23 23:14:27 +08:00
}
if len ( query . Tags ) > 0 {
filters = append ( filters , searchstore . TagsFilter { Tags : query . Tags } )
}
2022-06-03 03:56:01 +08:00
if len ( query . DashboardUIDs ) > 0 {
filters = append ( filters , searchstore . DashboardFilter { UIDs : query . DashboardUIDs } )
} else if len ( query . DashboardIds ) > 0 {
filters = append ( filters , searchstore . DashboardIDFilter { IDs : query . DashboardIds } )
2022-05-23 23:14:27 +08:00
}
if len ( query . Title ) > 0 {
2022-10-19 21:02:15 +08:00
filters = append ( filters , searchstore . TitleFilter { Dialect : d . store . GetDialect ( ) , Title : query . Title } )
2022-05-23 23:14:27 +08:00
}
if len ( query . Type ) > 0 {
2022-10-19 21:02:15 +08:00
filters = append ( filters , searchstore . TypeFilter { Dialect : d . store . GetDialect ( ) , Type : query . Type } )
2022-05-23 23:14:27 +08:00
}
2023-11-10 00:07:10 +08:00
// nolint:staticcheck
2022-05-23 23:14:27 +08:00
if len ( query . FolderIds ) > 0 {
filters = append ( filters , searchstore . FolderFilter { IDs : query . FolderIds } )
}
2023-08-04 17:43:47 +08:00
if len ( query . FolderUIDs ) > 0 {
2023-10-24 15:04:45 +08:00
filters = append ( filters , searchstore . FolderUIDFilter { Dialect : d . store . GetDialect ( ) , OrgID : orgID , UIDs : query . FolderUIDs , NestedFoldersEnabled : d . features . IsEnabled ( featuremgmt . FlagNestedFolders ) } )
2023-08-04 17:43:47 +08:00
}
2022-05-23 23:14:27 +08:00
var res [ ] dashboards . DashboardSearchProjection
2023-10-24 15:04:45 +08:00
sb := & searchstore . Builder { Dialect : d . store . GetDialect ( ) , Filters : filters , Features : d . features }
2022-05-23 23:14:27 +08:00
limit := query . Limit
if limit < 1 {
limit = 1000
}
page := query . Page
if page < 1 {
page = 1
}
sql , params := sb . ToSQL ( limit , page )
2023-07-19 17:37:27 +08:00
err = d . store . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-05-23 23:14:27 +08:00
return sess . SQL ( sql , params ... ) . Find ( & res )
} )
if err != nil {
return nil , err
}
return res , nil
}
2022-06-02 22:00:47 +08:00
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) GetDashboardTags ( ctx context . Context , query * dashboards . GetDashboardTagsQuery ) ( [ ] * dashboards . DashboardTagCloudItem , error ) {
2023-01-25 17:36:26 +08:00
queryResult := make ( [ ] * dashboards . DashboardTagCloudItem , 0 )
err := d . store . WithDbSession ( ctx , func ( dbSession * db . Session ) error {
2022-06-02 22:00:47 +08:00
sql := ` SELECT
COUNT ( * ) as count ,
term
FROM dashboard
INNER JOIN dashboard_tag on dashboard_tag . dashboard_id = dashboard . id
WHERE dashboard . org_id = ?
GROUP BY term
ORDER BY term `
2023-01-18 20:52:41 +08:00
sess := dbSession . SQL ( sql , query . OrgID )
2023-01-25 17:36:26 +08:00
err := sess . Find ( & queryResult )
2022-06-02 22:00:47 +08:00
return err
} )
2023-01-25 17:36:26 +08:00
if err != nil {
return nil , err
}
return queryResult , nil
2022-06-02 22:00:47 +08:00
}
2022-11-02 21:15:50 +08:00
2022-11-08 18:51:00 +08:00
// CountDashboardsInFolder returns a count of all dashboards associated with the
// given parent folder ID.
//
2022-11-02 21:15:50 +08:00
// This will be updated to take CountDashboardsInFolderQuery as an argument and
2022-11-08 18:51:00 +08:00
// lookup dashboards using the ParentFolderUID when dashboards are associated with a parent folder UID instead of ID.
2023-03-01 23:52:16 +08:00
func ( d * dashboardStore ) CountDashboardsInFolder (
2022-11-02 21:15:50 +08:00
ctx context . Context , req * dashboards . CountDashboardsInFolderRequest ) ( int64 , error ) {
2022-11-08 18:51:00 +08:00
var count int64
var err error
err = d . store . WithDbSession ( ctx , func ( sess * db . Session ) error {
2023-11-09 00:27:03 +08:00
// nolint:staticcheck
2022-11-02 21:15:50 +08:00
session := sess . In ( "folder_id" , req . FolderID ) . In ( "org_id" , req . OrgID ) .
In ( "is_folder" , d . store . GetDialect ( ) . BooleanStr ( false ) )
2023-01-18 20:52:41 +08:00
count , err = session . Count ( & dashboards . Dashboard { } )
2022-11-02 21:15:50 +08:00
return err
} )
2022-11-08 18:51:00 +08:00
return count , err
2022-11-02 21:15:50 +08:00
}
2022-11-15 03:08:10 +08:00
2023-04-14 17:17:23 +08:00
func ( d * dashboardStore ) DeleteDashboardsInFolder (
ctx context . Context , req * dashboards . DeleteDashboardsInFolderRequest ) error {
return d . store . WithTransactionalDbSession ( ctx , func ( sess * db . Session ) error {
dashboard := dashboards . Dashboard { OrgID : req . OrgID }
has , err := sess . Where ( "uid = ? AND org_id = ?" , req . FolderUID , req . OrgID ) . Get ( & dashboard )
if err != nil {
return err
}
if ! has {
return dashboards . ErrFolderNotFound
}
2023-07-10 17:12:04 +08:00
if err := d . deleteChildrenDashboardAssociations ( sess , & dashboard ) ; err != nil {
2023-04-14 17:17:23 +08:00
return err
}
_ , err = sess . Where ( "folder_id = ? AND org_id = ? AND is_folder = ?" , dashboard . ID , dashboard . OrgID , false ) . Delete ( & dashboards . Dashboard { } )
return err
} )
}
2022-11-15 03:08:10 +08:00
func readQuotaConfig ( cfg * setting . Cfg ) ( * quota . Map , error ) {
limits := & quota . Map { }
if cfg == nil {
return limits , nil
}
globalQuotaTag , err := quota . NewTag ( dashboards . QuotaTargetSrv , dashboards . QuotaTarget , quota . GlobalScope )
if err != nil {
return & quota . Map { } , err
}
orgQuotaTag , err := quota . NewTag ( dashboards . QuotaTargetSrv , dashboards . QuotaTarget , quota . OrgScope )
if err != nil {
return & quota . Map { } , err
}
limits . Set ( globalQuotaTag , cfg . Quota . Global . Dashboard )
limits . Set ( orgQuotaTag , cfg . Quota . Org . Dashboard )
return limits , nil
}