mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			493 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			493 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
package dashboards
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/grafana/grafana/pkg/components/simplejson"
 | 
						|
	"github.com/grafana/grafana/pkg/infra/slugify"
 | 
						|
	"github.com/grafana/grafana/pkg/kinds"
 | 
						|
	"github.com/grafana/grafana/pkg/kinds/dashboard"
 | 
						|
	"github.com/grafana/grafana/pkg/models"
 | 
						|
	"github.com/grafana/grafana/pkg/services/auth/identity"
 | 
						|
	"github.com/grafana/grafana/pkg/services/folder"
 | 
						|
	"github.com/grafana/grafana/pkg/services/org"
 | 
						|
	"github.com/grafana/grafana/pkg/services/quota"
 | 
						|
	"github.com/grafana/grafana/pkg/services/search/model"
 | 
						|
	"github.com/grafana/grafana/pkg/services/user"
 | 
						|
	"github.com/grafana/grafana/pkg/setting"
 | 
						|
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						|
)
 | 
						|
 | 
						|
const RootFolderName = "General"
 | 
						|
 | 
						|
const (
 | 
						|
	DashTypeDB       = "db"
 | 
						|
	DashTypeSnapshot = "snapshot"
 | 
						|
)
 | 
						|
 | 
						|
// Dashboard model
 | 
						|
type Dashboard struct {
 | 
						|
	ID       int64  `xorm:"pk autoincr 'id'"`
 | 
						|
	UID      string `xorm:"uid"`
 | 
						|
	Slug     string
 | 
						|
	OrgID    int64 `xorm:"org_id"`
 | 
						|
	GnetID   int64 `xorm:"gnet_id"`
 | 
						|
	Version  int
 | 
						|
	PluginID string `xorm:"plugin_id"`
 | 
						|
 | 
						|
	Created time.Time
 | 
						|
	Updated time.Time
 | 
						|
 | 
						|
	UpdatedBy int64
 | 
						|
	CreatedBy int64
 | 
						|
	FolderID  int64 `xorm:"folder_id"`
 | 
						|
	IsFolder  bool
 | 
						|
	HasACL    bool `xorm:"has_acl"`
 | 
						|
 | 
						|
	Title string
 | 
						|
	Data  *simplejson.Json
 | 
						|
}
 | 
						|
 | 
						|
func (d *Dashboard) SetID(id int64) {
 | 
						|
	d.ID = id
 | 
						|
	d.Data.Set("id", id)
 | 
						|
}
 | 
						|
 | 
						|
func (d *Dashboard) SetUID(uid string) {
 | 
						|
	d.UID = uid
 | 
						|
	d.Data.Set("uid", uid)
 | 
						|
}
 | 
						|
 | 
						|
func (d *Dashboard) SetVersion(version int) {
 | 
						|
	d.Version = version
 | 
						|
	d.Data.Set("version", version)
 | 
						|
}
 | 
						|
 | 
						|
func (d *Dashboard) ToResource() kinds.GrafanaResource[simplejson.Json, any] {
 | 
						|
	parent := dashboard.NewK8sResource(d.UID, nil)
 | 
						|
	res := kinds.GrafanaResource[simplejson.Json, any]{
 | 
						|
		Kind:       parent.Kind,
 | 
						|
		APIVersion: parent.APIVersion,
 | 
						|
		Metadata: kinds.GrafanaResourceMetadata{
 | 
						|
			Name:              d.UID,
 | 
						|
			Annotations:       make(map[string]string),
 | 
						|
			Labels:            make(map[string]string),
 | 
						|
			CreationTimestamp: v1.NewTime(d.Created),
 | 
						|
			ResourceVersion:   fmt.Sprintf("%d", d.Version),
 | 
						|
		},
 | 
						|
	}
 | 
						|
	if d.Data != nil {
 | 
						|
		copy := &simplejson.Json{}
 | 
						|
		db, _ := d.Data.ToDB()
 | 
						|
		_ = copy.FromDB(db)
 | 
						|
 | 
						|
		copy.Del("id")
 | 
						|
		copy.Del("version") // ???
 | 
						|
		copy.Del("uid")     // duplicated to name
 | 
						|
		res.Spec = copy
 | 
						|
	}
 | 
						|
 | 
						|
	d.UpdateSlug()
 | 
						|
	res.Metadata.SetUpdatedTimestamp(&d.Updated)
 | 
						|
	res.Metadata.SetSlug(d.Slug)
 | 
						|
	if d.CreatedBy > 0 {
 | 
						|
		res.Metadata.SetCreatedBy(fmt.Sprintf("user:%d", d.CreatedBy))
 | 
						|
	}
 | 
						|
	if d.UpdatedBy > 0 {
 | 
						|
		res.Metadata.SetUpdatedBy(fmt.Sprintf("user:%d", d.UpdatedBy))
 | 
						|
	}
 | 
						|
	if d.PluginID != "" {
 | 
						|
		res.Metadata.SetOriginInfo(&kinds.ResourceOriginInfo{
 | 
						|
			Name: "plugin",
 | 
						|
			Key:  d.PluginID,
 | 
						|
		})
 | 
						|
	}
 | 
						|
	if d.FolderID > 0 {
 | 
						|
		res.Metadata.SetFolder(fmt.Sprintf("folder:%d", d.FolderID))
 | 
						|
	}
 | 
						|
	if d.IsFolder {
 | 
						|
		res.Kind = "Folder"
 | 
						|
	}
 | 
						|
	return res
 | 
						|
}
 | 
						|
 | 
						|
// NewDashboard creates a new dashboard
 | 
						|
func NewDashboard(title string) *Dashboard {
 | 
						|
	dash := &Dashboard{}
 | 
						|
	dash.Data = simplejson.New()
 | 
						|
	dash.Data.Set("title", title)
 | 
						|
	dash.Title = title
 | 
						|
	dash.Created = time.Now()
 | 
						|
	dash.Updated = time.Now()
 | 
						|
	dash.UpdateSlug()
 | 
						|
	return dash
 | 
						|
}
 | 
						|
 | 
						|
// NewDashboardFolder creates a new dashboard folder
 | 
						|
func NewDashboardFolder(title string) *Dashboard {
 | 
						|
	folder := NewDashboard(title)
 | 
						|
	folder.IsFolder = true
 | 
						|
	folder.Data.Set("schemaVersion", 17)
 | 
						|
	folder.Data.Set("version", 0)
 | 
						|
	folder.IsFolder = true
 | 
						|
	return folder
 | 
						|
}
 | 
						|
 | 
						|
// GetTags turns the tags in data json into go string array
 | 
						|
func (d *Dashboard) GetTags() []string {
 | 
						|
	return d.Data.Get("tags").MustStringArray()
 | 
						|
}
 | 
						|
 | 
						|
func NewDashboardFromJson(data *simplejson.Json) *Dashboard {
 | 
						|
	dash := &Dashboard{}
 | 
						|
	dash.Data = data
 | 
						|
	dash.Title = dash.Data.Get("title").MustString()
 | 
						|
	dash.UpdateSlug()
 | 
						|
	update := false
 | 
						|
 | 
						|
	if id, err := dash.Data.Get("id").Float64(); err == nil {
 | 
						|
		dash.ID = int64(id)
 | 
						|
		update = true
 | 
						|
	}
 | 
						|
 | 
						|
	if uid, err := dash.Data.Get("uid").String(); err == nil {
 | 
						|
		dash.UID = uid
 | 
						|
		update = true
 | 
						|
	}
 | 
						|
 | 
						|
	if version, err := dash.Data.Get("version").Float64(); err == nil && update {
 | 
						|
		dash.Version = int(version)
 | 
						|
		dash.Updated = time.Now()
 | 
						|
	} else {
 | 
						|
		dash.Data.Set("version", 0)
 | 
						|
		dash.Created = time.Now()
 | 
						|
		dash.Updated = time.Now()
 | 
						|
	}
 | 
						|
 | 
						|
	if gnetId, err := dash.Data.Get("gnetId").Float64(); err == nil {
 | 
						|
		dash.GnetID = int64(gnetId)
 | 
						|
	}
 | 
						|
 | 
						|
	return dash
 | 
						|
}
 | 
						|
 | 
						|
// GetDashboardModel turns the command into the saveable model
 | 
						|
func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
 | 
						|
	dash := NewDashboardFromJson(cmd.Dashboard)
 | 
						|
	userID := cmd.UserID
 | 
						|
 | 
						|
	if userID == 0 {
 | 
						|
		userID = -1
 | 
						|
	}
 | 
						|
 | 
						|
	dash.UpdatedBy = userID
 | 
						|
	dash.OrgID = cmd.OrgID
 | 
						|
	dash.PluginID = cmd.PluginID
 | 
						|
	dash.IsFolder = cmd.IsFolder
 | 
						|
	dash.FolderID = cmd.FolderID
 | 
						|
	dash.UpdateSlug()
 | 
						|
	return dash
 | 
						|
}
 | 
						|
 | 
						|
// UpdateSlug updates the slug
 | 
						|
func (d *Dashboard) UpdateSlug() {
 | 
						|
	title := d.Data.Get("title").MustString()
 | 
						|
	d.Slug = slugify.Slugify(title)
 | 
						|
}
 | 
						|
 | 
						|
// GetURL return the html url for a folder if it's folder, otherwise for a dashboard
 | 
						|
func (d *Dashboard) GetURL() string {
 | 
						|
	return GetDashboardFolderURL(d.IsFolder, d.UID, d.Slug)
 | 
						|
}
 | 
						|
 | 
						|
// GetDashboardFolderURL return the html url for a folder if it's folder, otherwise for a dashboard
 | 
						|
func GetDashboardFolderURL(isFolder bool, uid string, slug string) string {
 | 
						|
	if isFolder {
 | 
						|
		return GetFolderURL(uid, slug)
 | 
						|
	}
 | 
						|
 | 
						|
	return GetDashboardURL(uid, slug)
 | 
						|
}
 | 
						|
 | 
						|
// GetDashboardURL returns the HTML url for a dashboard.
 | 
						|
func GetDashboardURL(uid string, slug string) string {
 | 
						|
	return fmt.Sprintf("%s/d/%s/%s", setting.AppSubUrl, uid, slug)
 | 
						|
}
 | 
						|
 | 
						|
// GetKioskModeDashboardUrl returns the HTML url for a dashboard in kiosk mode.
 | 
						|
func GetKioskModeDashboardURL(uid string, slug string, theme models.Theme) string {
 | 
						|
	return fmt.Sprintf("%s?kiosk&theme=%s", GetDashboardURL(uid, slug), string(theme))
 | 
						|
}
 | 
						|
 | 
						|
// GetFullDashboardURL returns the full URL for a dashboard.
 | 
						|
func GetFullDashboardURL(uid string, slug string) string {
 | 
						|
	return fmt.Sprintf("%sd/%s/%s", setting.AppUrl, uid, slug)
 | 
						|
}
 | 
						|
 | 
						|
// GetFolderURL returns the HTML url for a folder.
 | 
						|
func GetFolderURL(folderUID string, slug string) string {
 | 
						|
	return fmt.Sprintf("%s/dashboards/f/%s/%s", setting.AppSubUrl, folderUID, slug)
 | 
						|
}
 | 
						|
 | 
						|
type ValidateDashboardBeforeSaveResult struct {
 | 
						|
	IsParentFolderChanged bool
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// COMMANDS
 | 
						|
//
 | 
						|
 | 
						|
type SaveDashboardCommand struct {
 | 
						|
	Dashboard    *simplejson.Json `json:"dashboard" binding:"Required"`
 | 
						|
	UserID       int64            `json:"userId" xorm:"user_id"`
 | 
						|
	Overwrite    bool             `json:"overwrite"`
 | 
						|
	Message      string           `json:"message"`
 | 
						|
	OrgID        int64            `json:"-" xorm:"org_id"`
 | 
						|
	RestoredFrom int              `json:"-"`
 | 
						|
	PluginID     string           `json:"-" xorm:"plugin_id"`
 | 
						|
	FolderID     int64            `json:"folderId" xorm:"folder_id"`
 | 
						|
	FolderUID    string           `json:"folderUid" xorm:"folder_uid"`
 | 
						|
	IsFolder     bool             `json:"isFolder"`
 | 
						|
 | 
						|
	UpdatedAt time.Time
 | 
						|
}
 | 
						|
 | 
						|
type ValidateDashboardCommand struct {
 | 
						|
	Dashboard string `json:"dashboard" binding:"Required"`
 | 
						|
}
 | 
						|
 | 
						|
type TrimDashboardCommand struct {
 | 
						|
	Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
 | 
						|
	Meta      *simplejson.Json `json:"meta"`
 | 
						|
}
 | 
						|
 | 
						|
type DashboardProvisioning struct {
 | 
						|
	ID          int64 `xorm:"pk autoincr 'id'"`
 | 
						|
	DashboardID int64 `xorm:"dashboard_id"`
 | 
						|
	Name        string
 | 
						|
	ExternalID  string `xorm:"external_id"`
 | 
						|
	CheckSum    string
 | 
						|
	Updated     int64
 | 
						|
}
 | 
						|
 | 
						|
type DeleteDashboardCommand struct {
 | 
						|
	ID                     int64
 | 
						|
	OrgID                  int64
 | 
						|
	ForceDeleteFolderRules bool
 | 
						|
}
 | 
						|
 | 
						|
type DeleteOrphanedProvisionedDashboardsCommand struct {
 | 
						|
	ReaderNames []string
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// QUERIES
 | 
						|
//
 | 
						|
 | 
						|
// GetDashboardQuery is used to query for a single dashboard matching
 | 
						|
// a unique constraint within the provided OrgID.
 | 
						|
//
 | 
						|
// Available constraints:
 | 
						|
//   - ID uses Grafana's internal numeric database identifier to get a
 | 
						|
//     dashboard.
 | 
						|
//   - UID use the unique identifier to get a dashboard.
 | 
						|
//   - Title + FolderID uses the combination of the dashboard's
 | 
						|
//     human-readable title and its parent folder's ID
 | 
						|
//     (or zero, for top level items). Both are required if no other
 | 
						|
//     constraint is set.
 | 
						|
//
 | 
						|
// Multiple constraints can be combined.
 | 
						|
type GetDashboardQuery struct {
 | 
						|
	ID       int64
 | 
						|
	UID      string
 | 
						|
	Title    *string
 | 
						|
	FolderID *int64
 | 
						|
	OrgID    int64
 | 
						|
}
 | 
						|
 | 
						|
type DashboardTagCloudItem struct {
 | 
						|
	Term  string `json:"term"`
 | 
						|
	Count int    `json:"count"`
 | 
						|
}
 | 
						|
 | 
						|
type GetDashboardTagsQuery struct {
 | 
						|
	OrgID int64
 | 
						|
}
 | 
						|
 | 
						|
type GetDashboardsQuery struct {
 | 
						|
	DashboardIDs  []int64
 | 
						|
	DashboardUIDs []string
 | 
						|
	OrgID         int64
 | 
						|
}
 | 
						|
 | 
						|
type GetDashboardsByPluginIDQuery struct {
 | 
						|
	OrgID    int64
 | 
						|
	PluginID string
 | 
						|
}
 | 
						|
 | 
						|
type DashboardRef struct {
 | 
						|
	UID  string `xorm:"uid"`
 | 
						|
	Slug string
 | 
						|
}
 | 
						|
 | 
						|
type GetDashboardRefByIDQuery struct {
 | 
						|
	ID int64
 | 
						|
}
 | 
						|
 | 
						|
type SaveDashboardDTO struct {
 | 
						|
	OrgID     int64
 | 
						|
	UpdatedAt time.Time
 | 
						|
	User      identity.Requester
 | 
						|
	Message   string
 | 
						|
	Overwrite bool
 | 
						|
	Dashboard *Dashboard
 | 
						|
}
 | 
						|
 | 
						|
type DashboardSearchProjection struct {
 | 
						|
	ID          int64  `xorm:"id"`
 | 
						|
	UID         string `xorm:"uid"`
 | 
						|
	Title       string
 | 
						|
	Slug        string
 | 
						|
	Term        string
 | 
						|
	IsFolder    bool
 | 
						|
	FolderID    int64  `xorm:"folder_id"`
 | 
						|
	FolderUID   string `xorm:"folder_uid"`
 | 
						|
	FolderSlug  string
 | 
						|
	FolderTitle string
 | 
						|
	SortMeta    int64
 | 
						|
}
 | 
						|
 | 
						|
const (
 | 
						|
	QuotaTargetSrv quota.TargetSrv = "dashboard"
 | 
						|
	QuotaTarget    quota.Target    = "dashboard"
 | 
						|
)
 | 
						|
 | 
						|
type CountDashboardsInFolderQuery struct {
 | 
						|
	FolderUID string
 | 
						|
	OrgID     int64
 | 
						|
}
 | 
						|
 | 
						|
// TODO: CountDashboardsInFolderRequest is the request passed from the service
 | 
						|
// to the store layer. The FolderID will be replaced with FolderUID when
 | 
						|
// dashboards are updated with parent folder UIDs.
 | 
						|
type CountDashboardsInFolderRequest struct {
 | 
						|
	FolderID int64
 | 
						|
	OrgID    int64
 | 
						|
}
 | 
						|
 | 
						|
func FromDashboard(dash *Dashboard) *folder.Folder {
 | 
						|
	return &folder.Folder{
 | 
						|
		ID:        dash.ID,
 | 
						|
		UID:       dash.UID,
 | 
						|
		OrgID:     dash.OrgID,
 | 
						|
		Title:     dash.Title,
 | 
						|
		HasACL:    dash.HasACL,
 | 
						|
		URL:       GetFolderURL(dash.UID, dash.Slug),
 | 
						|
		Version:   dash.Version,
 | 
						|
		Created:   dash.Created,
 | 
						|
		CreatedBy: dash.CreatedBy,
 | 
						|
		Updated:   dash.Updated,
 | 
						|
		UpdatedBy: dash.UpdatedBy,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type DeleteDashboardsInFolderRequest struct {
 | 
						|
	FolderUID string
 | 
						|
	OrgID     int64
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// DASHBOARD ACL
 | 
						|
//
 | 
						|
 | 
						|
// Dashboard ACL model
 | 
						|
type DashboardACL struct {
 | 
						|
	ID          int64 `xorm:"pk autoincr 'id'"`
 | 
						|
	OrgID       int64 `xorm:"org_id"`
 | 
						|
	DashboardID int64 `xorm:"dashboard_id"`
 | 
						|
 | 
						|
	UserID     int64         `xorm:"user_id"`
 | 
						|
	TeamID     int64         `xorm:"team_id"`
 | 
						|
	Role       *org.RoleType // pointer to be nullable
 | 
						|
	Permission PermissionType
 | 
						|
 | 
						|
	Created time.Time
 | 
						|
	Updated time.Time
 | 
						|
}
 | 
						|
 | 
						|
func (p DashboardACL) TableName() string { return "dashboard_acl" }
 | 
						|
 | 
						|
type DashboardACLInfoDTO struct {
 | 
						|
	OrgID       int64  `json:"-" xorm:"org_id"`
 | 
						|
	DashboardID int64  `json:"dashboardId,omitempty" xorm:"dashboard_id"`
 | 
						|
	FolderID    int64  `json:"folderId,omitempty" xorm:"folder_id"`
 | 
						|
	FolderUID   string `json:"folderUid,omitempty" xorm:"folder_uid"`
 | 
						|
 | 
						|
	Created time.Time `json:"created"`
 | 
						|
	Updated time.Time `json:"updated"`
 | 
						|
 | 
						|
	UserID         int64          `json:"userId" xorm:"user_id"`
 | 
						|
	UserLogin      string         `json:"userLogin"`
 | 
						|
	UserEmail      string         `json:"userEmail"`
 | 
						|
	UserAvatarURL  string         `json:"userAvatarUrl" xorm:"user_avatar_url"`
 | 
						|
	TeamID         int64          `json:"teamId" xorm:"team_id"`
 | 
						|
	TeamEmail      string         `json:"teamEmail"`
 | 
						|
	TeamAvatarURL  string         `json:"teamAvatarUrl" xorm:"team_avatar_url"`
 | 
						|
	Team           string         `json:"team"`
 | 
						|
	Role           *org.RoleType  `json:"role,omitempty"`
 | 
						|
	Permission     PermissionType `json:"permission"`
 | 
						|
	PermissionName string         `json:"permissionName"`
 | 
						|
	UID            string         `json:"uid" xorm:"uid"`
 | 
						|
	Title          string         `json:"title"`
 | 
						|
	Slug           string         `json:"slug"`
 | 
						|
	IsFolder       bool           `json:"isFolder"`
 | 
						|
	URL            string         `json:"url" xorm:"url"`
 | 
						|
	Inherited      bool           `json:"inherited"`
 | 
						|
}
 | 
						|
 | 
						|
func (dto *DashboardACLInfoDTO) hasSameRoleAs(other *DashboardACLInfoDTO) bool {
 | 
						|
	if dto.Role == nil || other.Role == nil {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	return dto.UserID <= 0 && dto.TeamID <= 0 && dto.UserID == other.UserID && dto.TeamID == other.TeamID && *dto.Role == *other.Role
 | 
						|
}
 | 
						|
 | 
						|
func (dto *DashboardACLInfoDTO) hasSameUserAs(other *DashboardACLInfoDTO) bool {
 | 
						|
	return dto.UserID > 0 && dto.UserID == other.UserID
 | 
						|
}
 | 
						|
 | 
						|
func (dto *DashboardACLInfoDTO) hasSameTeamAs(other *DashboardACLInfoDTO) bool {
 | 
						|
	return dto.TeamID > 0 && dto.TeamID == other.TeamID
 | 
						|
}
 | 
						|
 | 
						|
// IsDuplicateOf returns true if other item has same role, same user or same team
 | 
						|
func (dto *DashboardACLInfoDTO) IsDuplicateOf(other *DashboardACLInfoDTO) bool {
 | 
						|
	return dto.hasSameRoleAs(other) || dto.hasSameUserAs(other) || dto.hasSameTeamAs(other)
 | 
						|
}
 | 
						|
 | 
						|
// QUERIES
 | 
						|
type GetDashboardACLInfoListQuery struct {
 | 
						|
	DashboardID int64
 | 
						|
	OrgID       int64
 | 
						|
}
 | 
						|
 | 
						|
type FindPersistedDashboardsQuery struct {
 | 
						|
	Title         string
 | 
						|
	OrgId         int64
 | 
						|
	SignedInUser  *user.SignedInUser
 | 
						|
	DashboardIds  []int64
 | 
						|
	DashboardUIDs []string
 | 
						|
	Type          string
 | 
						|
	FolderIds     []int64
 | 
						|
	FolderUIDs    []string
 | 
						|
	Tags          []string
 | 
						|
	Limit         int64
 | 
						|
	Page          int64
 | 
						|
	Permission    PermissionType
 | 
						|
	Sort          model.SortOption
 | 
						|
 | 
						|
	Filters []any
 | 
						|
}
 |