2022-06-02 18:27:23 -08:00
package database
import (
"context"
2022-09-13 13:33:41 -03:00
"encoding/json"
2022-06-02 18:27:23 -08:00
2022-10-19 09:02:15 -04:00
"github.com/grafana/grafana/pkg/infra/db"
2022-07-06 15:51:44 -08:00
"github.com/grafana/grafana/pkg/infra/log"
2022-06-30 09:31:54 -04:00
"github.com/grafana/grafana/pkg/services/dashboards"
2023-11-22 14:20:22 +01:00
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
2023-06-21 10:48:09 -03:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/org"
2022-07-06 15:51:44 -08:00
"github.com/grafana/grafana/pkg/services/publicdashboards"
. "github.com/grafana/grafana/pkg/services/publicdashboards/models"
2023-03-08 14:54:35 -06:00
"github.com/grafana/grafana/pkg/services/sqlstore"
2023-06-21 10:48:09 -03:00
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/setting"
2022-06-02 18:27:23 -08:00
)
2022-07-06 15:51:44 -08:00
// Define the storage implementation. We're generating the mock implementation
// automatically
type PublicDashboardStoreImpl struct {
2022-10-14 15:33:06 -04:00
sqlStore db . DB
2022-07-06 15:51:44 -08:00
log log . Logger
2023-06-21 10:48:09 -03:00
cfg * setting . Cfg
features featuremgmt . FeatureToggles
2022-07-06 15:51:44 -08:00
}
2022-08-01 14:46:48 -08:00
var LogPrefix = "publicdashboards.store"
2022-07-06 15:51:44 -08:00
// Gives us a compile time error if our database does not adhere to contract of
// the interface
var _ publicdashboards . Store = ( * PublicDashboardStoreImpl ) ( nil )
// Factory used by wire to dependency injection
2023-06-21 10:48:09 -03:00
func ProvideStore ( sqlStore db . DB , cfg * setting . Cfg , features featuremgmt . FeatureToggles ) * PublicDashboardStoreImpl {
2022-07-06 15:51:44 -08:00
return & PublicDashboardStoreImpl {
sqlStore : sqlStore ,
2022-08-01 14:46:48 -08:00
log : log . New ( LogPrefix ) ,
2023-06-21 10:48:09 -03:00
cfg : cfg ,
features : features ,
2022-07-06 15:51:44 -08:00
}
}
2023-06-21 10:48:09 -03:00
// FindAllWithPagination Returns a list of public dashboards by orgId, based on permissions and with pagination
func ( d * PublicDashboardStoreImpl ) FindAllWithPagination ( ctx context . Context , query * PublicDashboardListQuery ) ( * PublicDashboardListResponseWithPagination , error ) {
resp := & PublicDashboardListResponseWithPagination {
PublicDashboards : make ( [ ] * PublicDashboardListResponse , 0 ) ,
TotalCount : 0 ,
}
2022-10-12 21:36:05 -08:00
2023-06-21 10:48:09 -03:00
recursiveQueriesAreSupported , err := d . sqlStore . RecursiveQueriesAreSupported ( )
if err != nil {
return nil , err
}
pubdashBuilder := db . NewSqlBuilder ( d . cfg , d . features , d . sqlStore . GetDialect ( ) , recursiveQueriesAreSupported )
pubdashBuilder . Write ( "SELECT dashboard_public.uid, dashboard_public.access_token, dashboard.uid as dashboard_uid, dashboard_public.is_enabled, dashboard.title" )
pubdashBuilder . Write ( " FROM dashboard_public" )
pubdashBuilder . Write ( " JOIN dashboard ON dashboard.uid = dashboard_public.dashboard_uid AND dashboard.org_id = dashboard_public.org_id" )
pubdashBuilder . Write ( ` WHERE dashboard_public.org_id = ? ` , query . OrgID )
if query . User . OrgRole != org . RoleAdmin {
2023-11-22 14:20:22 +01:00
pubdashBuilder . WriteDashboardPermissionFilter ( query . User , dashboardaccess . PERMISSION_VIEW , searchstore . TypeDashboard )
2023-06-21 10:48:09 -03:00
}
pubdashBuilder . Write ( " ORDER BY dashboard.title" )
pubdashBuilder . Write ( d . sqlStore . GetDialect ( ) . LimitOffset ( int64 ( query . Limit ) , int64 ( query . Offset ) ) )
counterBuilder := db . NewSqlBuilder ( d . cfg , d . features , d . sqlStore . GetDialect ( ) , recursiveQueriesAreSupported )
counterBuilder . Write ( "SELECT COUNT(*)" )
counterBuilder . Write ( " FROM dashboard_public" )
counterBuilder . Write ( " JOIN dashboard ON dashboard.uid = dashboard_public.dashboard_uid AND dashboard.org_id = dashboard_public.org_id" )
counterBuilder . Write ( ` WHERE dashboard_public.org_id = ? ` , query . OrgID )
if query . User . OrgRole != org . RoleAdmin {
2023-11-22 14:20:22 +01:00
counterBuilder . WriteDashboardPermissionFilter ( query . User , dashboardaccess . PERMISSION_VIEW , searchstore . TypeDashboard )
2023-06-21 10:48:09 -03:00
}
err = d . sqlStore . WithDbSession ( ctx , func ( sess * db . Session ) error {
err := sess . SQL ( pubdashBuilder . GetSQLString ( ) , pubdashBuilder . GetParams ( ) ... ) . Find ( & resp . PublicDashboards )
if err != nil {
return err
}
2022-10-12 21:36:05 -08:00
2023-06-21 10:48:09 -03:00
_ , err = sess . SQL ( counterBuilder . GetSQLString ( ) , counterBuilder . GetParams ( ) ... ) . Get ( & resp . TotalCount )
2022-10-12 21:36:05 -08:00
return err
} )
if err != nil {
return nil , err
}
return resp , nil
}
2022-10-27 17:08:11 -08:00
// Find Returns public dashboard by Uid or nil if not found
2022-10-25 21:40:42 -03:00
func ( d * PublicDashboardStoreImpl ) Find ( ctx context . Context , uid string ) ( * PublicDashboard , error ) {
2022-10-25 16:29:18 -03:00
if uid == "" {
return nil , nil
}
2022-06-22 13:58:52 -08:00
2022-10-25 16:29:18 -03:00
var found bool
2022-11-03 11:30:12 -08:00
publicDashboard := & PublicDashboard { Uid : uid }
2022-10-31 18:16:07 -03:00
err := d . sqlStore . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-10-25 16:29:18 -03:00
var err error
2022-11-03 11:30:12 -08:00
found , err = sess . Get ( publicDashboard )
2022-10-25 16:29:18 -03:00
return err
2022-06-22 13:58:52 -08:00
} )
if err != nil {
2022-10-25 16:29:18 -03:00
return nil , err
2022-06-02 18:27:23 -08:00
}
2022-10-25 16:29:18 -03:00
if ! found {
return nil , nil
2022-10-13 11:32:32 -03:00
}
2022-11-03 11:30:12 -08:00
return publicDashboard , nil
2022-10-13 11:32:32 -03:00
}
2022-10-26 12:53:33 -03:00
// FindByAccessToken Returns public dashboard by access token or nil if not found
2022-10-25 21:40:42 -03:00
func ( d * PublicDashboardStoreImpl ) FindByAccessToken ( ctx context . Context , accessToken string ) ( * PublicDashboard , error ) {
2022-10-25 16:29:18 -03:00
if accessToken == "" {
2022-10-31 11:22:27 -03:00
return nil , nil
2022-08-26 11:28:54 -08:00
}
var found bool
2022-11-03 11:30:12 -08:00
publicDashboard := & PublicDashboard { AccessToken : accessToken }
2022-10-31 18:16:07 -03:00
err := d . sqlStore . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-08-26 11:28:54 -08:00
var err error
2022-11-03 11:30:12 -08:00
found , err = sess . Get ( publicDashboard )
2022-08-26 11:28:54 -08:00
return err
} )
if err != nil {
return nil , err
}
if ! found {
return nil , nil
}
2022-11-03 11:30:12 -08:00
return publicDashboard , nil
2022-08-26 11:28:54 -08:00
}
2022-10-31 11:22:27 -03:00
// FindByDashboardUid Retrieves public dashboard by dashboard uid or nil if not found
2022-10-25 21:40:42 -03:00
func ( d * PublicDashboardStoreImpl ) FindByDashboardUid ( ctx context . Context , orgId int64 , dashboardUid string ) ( * PublicDashboard , error ) {
2022-10-31 11:22:27 -03:00
if dashboardUid == "" || orgId == 0 {
return nil , nil
2022-06-02 18:27:23 -08:00
}
2022-10-31 11:22:27 -03:00
var found bool
2022-10-31 18:16:07 -03:00
publicDashboard := & PublicDashboard { OrgId : orgId , DashboardUid : dashboardUid }
err := d . sqlStore . WithDbSession ( ctx , func ( sess * db . Session ) error {
2022-10-31 11:22:27 -03:00
var err error
2022-10-31 18:16:07 -03:00
found , err = sess . Get ( publicDashboard )
2022-06-02 18:27:23 -08:00
if err != nil {
return err
}
return nil
} )
if err != nil {
return nil , err
}
2022-10-31 11:22:27 -03:00
if ! found {
return nil , nil
}
2022-11-03 11:30:12 -08:00
return publicDashboard , nil
2022-10-31 18:16:07 -03:00
}
2022-10-25 21:40:42 -03:00
// ExistsEnabledByDashboardUid Responds true if there is an enabled public dashboard for a dashboard uid
func ( d * PublicDashboardStoreImpl ) ExistsEnabledByDashboardUid ( ctx context . Context , dashboardUid string ) ( bool , error ) {
2022-07-19 17:44:41 -06:00
hasPublicDashboard := false
2022-10-19 09:02:15 -04:00
err := d . sqlStore . WithDbSession ( ctx , func ( dbSession * db . Session ) error {
2022-07-19 17:44:41 -06:00
sql := "SELECT COUNT(*) FROM dashboard_public WHERE dashboard_uid=? AND is_enabled=true"
result , err := dbSession . SQL ( sql , dashboardUid ) . Count ( )
if err != nil {
return err
}
hasPublicDashboard = result > 0
return err
} )
return hasPublicDashboard , err
}
2022-08-10 11:14:48 -06:00
2022-10-25 21:40:42 -03:00
// ExistsEnabledByAccessToken Responds true if the accessToken exists and the public dashboard is enabled
func ( d * PublicDashboardStoreImpl ) ExistsEnabledByAccessToken ( ctx context . Context , accessToken string ) ( bool , error ) {
2022-08-10 11:14:48 -06:00
hasPublicDashboard := false
2022-10-19 09:02:15 -04:00
err := d . sqlStore . WithDbSession ( ctx , func ( dbSession * db . Session ) error {
2022-08-10 11:14:48 -06:00
sql := "SELECT COUNT(*) FROM dashboard_public WHERE access_token=? AND is_enabled=true"
result , err := dbSession . SQL ( sql , accessToken ) . Count ( )
if err != nil {
return err
}
hasPublicDashboard = result > 0
return err
} )
return hasPublicDashboard , err
}
2022-10-06 12:35:19 -08:00
2023-03-03 10:12:29 -03:00
// GetOrgIdByAccessToken Returns the public dashboard OrgId if exists.
2022-10-25 21:40:42 -03:00
func ( d * PublicDashboardStoreImpl ) GetOrgIdByAccessToken ( ctx context . Context , accessToken string ) ( int64 , error ) {
2022-10-06 12:35:19 -08:00
var orgId int64
2022-10-19 09:02:15 -04:00
err := d . sqlStore . WithDbSession ( ctx , func ( dbSession * db . Session ) error {
2023-03-03 10:12:29 -03:00
sql := "SELECT org_id FROM dashboard_public WHERE access_token=?"
2022-10-06 12:35:19 -08:00
_ , err := dbSession . SQL ( sql , accessToken ) . Get ( & orgId )
if err != nil {
return err
}
return err
} )
return orgId , err
}
2022-11-03 11:30:12 -08:00
// Creates a public dashboard
func ( d * PublicDashboardStoreImpl ) Create ( ctx context . Context , cmd SavePublicDashboardCommand ) ( int64 , error ) {
if cmd . PublicDashboard . DashboardUid == "" {
return 0 , dashboards . ErrDashboardIdentifierNotSet
}
var affectedRows int64
err := d . sqlStore . WithDbSession ( ctx , func ( sess * db . Session ) error {
var err error
affectedRows , err = sess . UseBool ( "is_enabled" ) . Insert ( & cmd . PublicDashboard )
return err
} )
return affectedRows , err
}
// Updates existing public dashboard
func ( d * PublicDashboardStoreImpl ) Update ( ctx context . Context , cmd SavePublicDashboardCommand ) ( int64 , error ) {
var affectedRows int64
err := d . sqlStore . WithDbSession ( ctx , func ( sess * db . Session ) error {
timeSettingsJSON , err := json . Marshal ( cmd . PublicDashboard . TimeSettings )
if err != nil {
return err
}
2023-02-09 15:44:09 -03:00
sqlResult , err := sess . Exec ( "UPDATE dashboard_public SET is_enabled = ?, annotations_enabled = ?, time_selection_enabled = ?, share = ?, time_settings = ?, updated_by = ?, updated_at = ? WHERE uid = ?" ,
2022-11-03 11:30:12 -08:00
cmd . PublicDashboard . IsEnabled ,
cmd . PublicDashboard . AnnotationsEnabled ,
2022-12-01 11:37:08 -03:00
cmd . PublicDashboard . TimeSelectionEnabled ,
2023-02-09 15:44:09 -03:00
cmd . PublicDashboard . Share ,
2022-11-03 11:30:12 -08:00
string ( timeSettingsJSON ) ,
cmd . PublicDashboard . UpdatedBy ,
cmd . PublicDashboard . UpdatedAt . UTC ( ) . Format ( "2006-01-02 15:04:05" ) ,
cmd . PublicDashboard . Uid )
if err != nil {
return err
}
affectedRows , err = sqlResult . RowsAffected ( )
return err
} )
return affectedRows , err
}
// Deletes a public dashboard
2023-03-08 14:54:35 -06:00
func ( d * PublicDashboardStoreImpl ) Delete ( ctx context . Context , uid string ) ( int64 , error ) {
dashboard := & PublicDashboard { Uid : uid }
2022-11-03 11:30:12 -08:00
var affectedRows int64
err := d . sqlStore . WithDbSession ( ctx , func ( sess * db . Session ) error {
var err error
affectedRows , err = sess . Delete ( dashboard )
return err
} )
return affectedRows , err
}
2023-03-08 14:54:35 -06:00
func ( d * PublicDashboardStoreImpl ) FindByDashboardFolder ( ctx context . Context , dashboard * dashboards . Dashboard ) ( [ ] * PublicDashboard , error ) {
if dashboard == nil || ! dashboard . IsFolder {
return nil , nil
}
var pubdashes [ ] * PublicDashboard
err := d . sqlStore . WithDbSession ( ctx , func ( sess * sqlstore . DBSession ) error {
return sess . SQL ( "SELECT * from dashboard_public WHERE (dashboard_uid, org_id) IN (SELECT uid, org_id FROM dashboard WHERE folder_id = ?)" , dashboard . ID ) . Find ( & pubdashes )
} )
if err != nil {
return nil , err
}
return pubdashes , nil
}
2023-04-11 14:36:50 -03:00
func ( d * PublicDashboardStoreImpl ) GetMetrics ( ctx context . Context ) ( * Metrics , error ) {
metrics := & Metrics {
TotalPublicDashboards : [ ] * TotalPublicDashboard { } ,
}
err := d . sqlStore . WithDbSession ( ctx , func ( sess * db . Session ) error {
return sess . SQL ( "SELECT COUNT(*) as total_count, is_enabled, share as share_type FROM dashboard_public GROUP BY is_enabled, share" ) . Find ( & metrics . TotalPublicDashboards )
} )
if err != nil {
return nil , err
}
return metrics , nil
}