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-02 18:27:23 -08:00
|
|
|
"github.com/grafana/grafana/pkg/models"
|
2022-06-30 09:31:54 -04:00
|
|
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
2022-07-06 15:51:44 -08:00
|
|
|
"github.com/grafana/grafana/pkg/services/publicdashboards"
|
2022-10-13 11:32:32 -03:00
|
|
|
"github.com/grafana/grafana/pkg/services/publicdashboards/internal/tokens"
|
2022-07-06 15:51:44 -08:00
|
|
|
. "github.com/grafana/grafana/pkg/services/publicdashboards/models"
|
2022-06-02 18:27:23 -08:00
|
|
|
"github.com/grafana/grafana/pkg/util"
|
|
|
|
|
)
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
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
|
2022-10-14 15:33:06 -04:00
|
|
|
func ProvideStore(sqlStore db.DB) *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),
|
2022-07-06 15:51:44 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-12 21:36:05 -08:00
|
|
|
// Gets list of public dashboards by orgId
|
|
|
|
|
func (d *PublicDashboardStoreImpl) ListPublicDashboards(ctx context.Context, orgId int64) ([]PublicDashboardListResponse, error) {
|
|
|
|
|
resp := make([]PublicDashboardListResponse, 0)
|
|
|
|
|
|
2022-10-19 09:02:15 -04:00
|
|
|
err := d.sqlStore.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
2022-10-12 21:36:05 -08:00
|
|
|
sess.Table("dashboard_public").
|
|
|
|
|
Join("LEFT", "dashboard", "dashboard.uid = dashboard_public.dashboard_uid AND dashboard.org_id = dashboard_public.org_id").
|
|
|
|
|
Cols("dashboard_public.uid", "dashboard_public.access_token", "dashboard_public.dashboard_uid", "dashboard_public.is_enabled", "dashboard.title").
|
|
|
|
|
Where("dashboard_public.org_id = ?", orgId).
|
|
|
|
|
OrderBy("is_enabled DESC, dashboard.title ASC")
|
|
|
|
|
|
|
|
|
|
err := sess.Find(&resp)
|
|
|
|
|
return err
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-21 13:56:20 -06:00
|
|
|
func (d *PublicDashboardStoreImpl) GetDashboard(ctx context.Context, dashboardUid string) (*models.Dashboard, error) {
|
|
|
|
|
dashboard := &models.Dashboard{Uid: dashboardUid}
|
2022-10-19 09:02:15 -04:00
|
|
|
err := d.sqlStore.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
2022-07-21 13:56:20 -06:00
|
|
|
has, err := sess.Get(dashboard)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !has {
|
|
|
|
|
return ErrPublicDashboardNotFound
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return dashboard, err
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-06 12:35:19 -08:00
|
|
|
// Retrieves public dashboard. This method makes 2 queries to the db so that in the
|
|
|
|
|
// even that the underlying dashboard is missing it will return a 404.
|
2022-07-06 15:51:44 -08:00
|
|
|
func (d *PublicDashboardStoreImpl) GetPublicDashboard(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error) {
|
2022-06-22 13:58:52 -08:00
|
|
|
if accessToken == "" {
|
2022-07-06 15:51:44 -08:00
|
|
|
return nil, nil, ErrPublicDashboardIdentifierNotSet
|
2022-06-02 18:27:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get public dashboard
|
2022-07-06 15:51:44 -08:00
|
|
|
pdRes := &PublicDashboard{AccessToken: accessToken}
|
2022-10-19 09:02:15 -04:00
|
|
|
err := d.sqlStore.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
2022-06-02 18:27:23 -08:00
|
|
|
has, err := sess.Get(pdRes)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !has {
|
2022-07-06 15:51:44 -08:00
|
|
|
return ErrPublicDashboardNotFound
|
2022-06-02 18:27:23 -08:00
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find dashboard
|
2022-07-21 13:56:20 -06:00
|
|
|
dashRes, err := d.GetDashboard(ctx, pdRes.DashboardUid)
|
2022-06-02 18:27:23 -08:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pdRes, dashRes, err
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-06 15:51:44 -08:00
|
|
|
// Generates a new unique uid to retrieve a public dashboard
|
|
|
|
|
func (d *PublicDashboardStoreImpl) GenerateNewPublicDashboardUid(ctx context.Context) (string, error) {
|
2022-06-22 13:58:52 -08:00
|
|
|
var uid string
|
2022-06-02 18:27:23 -08:00
|
|
|
|
2022-10-19 09:02:15 -04:00
|
|
|
err := d.sqlStore.WithDbSession(ctx, func(sess *db.Session) error {
|
2022-06-22 13:58:52 -08:00
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
uid = util.GenerateShortUID()
|
|
|
|
|
|
2022-07-06 15:51:44 -08:00
|
|
|
exists, err := sess.Get(&PublicDashboard{Uid: uid})
|
2022-06-22 13:58:52 -08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2022-06-02 18:27:23 -08:00
|
|
|
|
2022-06-22 13:58:52 -08:00
|
|
|
if !exists {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2022-06-02 18:27:23 -08:00
|
|
|
}
|
2022-06-22 13:58:52 -08:00
|
|
|
|
2022-07-06 15:51:44 -08:00
|
|
|
return ErrPublicDashboardFailedGenerateUniqueUid
|
2022-06-22 13:58:52 -08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
2022-06-02 18:27:23 -08:00
|
|
|
}
|
|
|
|
|
|
2022-06-22 13:58:52 -08:00
|
|
|
return uid, nil
|
2022-06-02 18:27:23 -08:00
|
|
|
}
|
|
|
|
|
|
2022-10-13 11:32:32 -03:00
|
|
|
// Generates a new unique access token for a new public dashboard
|
|
|
|
|
func (d *PublicDashboardStoreImpl) GenerateNewPublicDashboardAccessToken(ctx context.Context) (string, error) {
|
|
|
|
|
var accessToken string
|
|
|
|
|
|
2022-10-19 09:02:15 -04:00
|
|
|
err := d.sqlStore.WithDbSession(ctx, func(sess *db.Session) error {
|
2022-10-13 11:32:32 -03:00
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
var err error
|
|
|
|
|
accessToken, err = tokens.GenerateAccessToken()
|
|
|
|
|
if err != nil {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exists, err := sess.Get(&PublicDashboard{AccessToken: accessToken})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !exists {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ErrPublicDashboardFailedGenerateAccessToken
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return accessToken, nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-26 11:28:54 -08:00
|
|
|
// Retrieves public dashboard configuration by Uid
|
|
|
|
|
func (d *PublicDashboardStoreImpl) GetPublicDashboardByUid(ctx context.Context, uid string) (*PublicDashboard, error) {
|
|
|
|
|
if uid == "" {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var found bool
|
|
|
|
|
pdRes := &PublicDashboard{Uid: uid}
|
2022-10-19 09:02:15 -04:00
|
|
|
err := d.sqlStore.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
2022-08-26 11:28:54 -08:00
|
|
|
var err error
|
|
|
|
|
found, err = sess.Get(pdRes)
|
|
|
|
|
return err
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !found {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pdRes, err
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-06 15:51:44 -08:00
|
|
|
// Retrieves public dashboard configuration
|
|
|
|
|
func (d *PublicDashboardStoreImpl) GetPublicDashboardConfig(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error) {
|
2022-06-02 18:27:23 -08:00
|
|
|
if dashboardUid == "" {
|
2022-06-30 09:31:54 -04:00
|
|
|
return nil, dashboards.ErrDashboardIdentifierNotSet
|
2022-06-02 18:27:23 -08:00
|
|
|
}
|
|
|
|
|
|
2022-07-06 15:51:44 -08:00
|
|
|
pdRes := &PublicDashboard{OrgId: orgId, DashboardUid: dashboardUid}
|
2022-10-19 09:02:15 -04:00
|
|
|
err := d.sqlStore.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
2022-06-02 18:27:23 -08:00
|
|
|
// publicDashboard
|
2022-06-22 13:58:52 -08:00
|
|
|
_, err := sess.Get(pdRes)
|
2022-06-02 18:27:23 -08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-22 13:58:52 -08:00
|
|
|
return pdRes, err
|
2022-06-02 18:27:23 -08:00
|
|
|
}
|
|
|
|
|
|
2022-07-06 15:51:44 -08:00
|
|
|
// Persists public dashboard configuration
|
2022-08-26 11:28:54 -08:00
|
|
|
func (d *PublicDashboardStoreImpl) SavePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) error {
|
|
|
|
|
if cmd.PublicDashboard.DashboardUid == "" {
|
|
|
|
|
return dashboards.ErrDashboardIdentifierNotSet
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-19 09:02:15 -04:00
|
|
|
err := d.sqlStore.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
2022-06-22 13:58:52 -08:00
|
|
|
_, err := sess.UseBool("is_enabled").Insert(&cmd.PublicDashboard)
|
2022-06-02 18:27:23 -08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-22 13:58:52 -08:00
|
|
|
return nil
|
|
|
|
|
})
|
2022-06-02 18:27:23 -08:00
|
|
|
|
2022-08-26 11:28:54 -08:00
|
|
|
return err
|
2022-06-22 13:58:52 -08:00
|
|
|
}
|
2022-06-02 18:27:23 -08:00
|
|
|
|
2022-10-06 12:35:19 -08:00
|
|
|
// Updates existing public dashboard configuration
|
2022-07-06 15:51:44 -08:00
|
|
|
func (d *PublicDashboardStoreImpl) UpdatePublicDashboardConfig(ctx context.Context, cmd SavePublicDashboardConfigCommand) error {
|
2022-10-19 09:02:15 -04:00
|
|
|
err := d.sqlStore.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
2022-09-13 13:33:41 -03:00
|
|
|
timeSettingsJSON, err := json.Marshal(cmd.PublicDashboard.TimeSettings)
|
2022-06-30 18:30:01 -03:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = sess.Exec("UPDATE dashboard_public SET is_enabled = ?, time_settings = ?, updated_by = ?, updated_at = ? WHERE uid = ?",
|
|
|
|
|
cmd.PublicDashboard.IsEnabled,
|
|
|
|
|
string(timeSettingsJSON),
|
|
|
|
|
cmd.PublicDashboard.UpdatedBy,
|
|
|
|
|
cmd.PublicDashboard.UpdatedAt.UTC().Format("2006-01-02 15:04:05"),
|
|
|
|
|
cmd.PublicDashboard.Uid)
|
|
|
|
|
|
2022-06-02 18:27:23 -08:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
|
2022-06-22 13:58:52 -08:00
|
|
|
return err
|
2022-06-02 18:27:23 -08:00
|
|
|
}
|
2022-07-19 17:44:41 -06:00
|
|
|
|
2022-10-06 12:35:19 -08:00
|
|
|
// Responds true if public dashboard for a dashboard exists and isEnabled
|
2022-07-19 17:44:41 -06:00
|
|
|
func (d *PublicDashboardStoreImpl) PublicDashboardEnabled(ctx context.Context, dashboardUid string) (bool, error) {
|
|
|
|
|
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-06 12:35:19 -08:00
|
|
|
// Responds true if accessToken exists and isEnabled. May be renamed in the
|
|
|
|
|
// future
|
2022-08-10 11:14:48 -06:00
|
|
|
func (d *PublicDashboardStoreImpl) AccessTokenExists(ctx context.Context, accessToken string) (bool, error) {
|
|
|
|
|
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
|
|
|
|
|
|
|
|
// Responds with OrgId from if exists and isEnabled.
|
|
|
|
|
func (d *PublicDashboardStoreImpl) GetPublicDashboardOrgId(ctx context.Context, accessToken string) (int64, error) {
|
|
|
|
|
var orgId int64
|
2022-10-19 09:02:15 -04:00
|
|
|
err := d.sqlStore.WithDbSession(ctx, func(dbSession *db.Session) error {
|
2022-10-06 12:35:19 -08:00
|
|
|
sql := "SELECT org_id FROM dashboard_public WHERE access_token=? AND is_enabled=true"
|
|
|
|
|
|
|
|
|
|
_, err := dbSession.SQL(sql, accessToken).Get(&orgId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return orgId, err
|
|
|
|
|
}
|