mirror of
https://github.com/grafana/grafana.git
synced 2025-02-16 18:34:52 -06:00
* dashboards: new command for validating dashboard before update Removes validation logic from saveDashboard and later on use the new command for validating dashboard before saving a dashboard. This due to the fact that we need to validate permissions for overwriting other dashboards by uid and title. * dashboards: use the new command for validating dashboard before saving Had to refactor dashboard provisioning a bit to be able to sidetrack the permission validation in a somewhat reasonable way. Adds some initial tests of the dashboard repository, but needs to be extended later. At least now you can mock the dashboard guardian * dashboards: removes validation logic in the save dashboard api layer Use the dashboard repository solely for create/update dashboards and let it do all the validation. One exception regarding quota validation which still is in api layer since that logic is in a macaron middleware. Need to move out-commented api tests later. * dashboards: fix database tests for validate and saving dashboards * dashboards: rename dashboard repository to dashboard service Split the old dashboard repository interface in two new interfaces, IDashboardService and IDashboardProvisioningService. Makes it more explicit when using it from the provisioning package and there's no possibility of calling an incorrect method for saving a dashboard. * database: make the InitTestDB function available to use from other packages * dashboards: rename ValidateDashboardForUpdateCommand and some refactoring * dashboards: integration tests of dashboard service * dashboard: fix sqlstore test due to folder exist validation * dashboards: move dashboard service integration tests to sqlstore package Had to move it to the sqlstore package due to concurrency problems when running against mysql and postgres. Using InitTestDB from two packages added conflicts when clearing and running migrations on the test database * dashboards: refactor how to find id to be used for save permission check * dashboards: remove duplicated dashboard tests * dashboards: cleanup dashboard service integration tests * dashboards: handle save dashboard errors and return correct http status * fix: remove log statement * dashboards: import dashboard should use dashboard service Had to move alerting commands to models package due to problems with import cycles of packages. * dashboards: cleanup dashboard api tests and add some tests for post dashboard * dashboards: rename dashboard service interfaces * dashboards: rename dashboard guardian interface
352 lines
8.8 KiB
Go
352 lines
8.8 KiB
Go
package models
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gosimple/slug"
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
// Typed errors
|
|
var (
|
|
ErrDashboardNotFound = errors.New("Dashboard not found")
|
|
ErrFolderNotFound = errors.New("Folder not found")
|
|
ErrDashboardSnapshotNotFound = errors.New("Dashboard snapshot not found")
|
|
ErrDashboardWithSameUIDExists = errors.New("A dashboard with the same uid already exists")
|
|
ErrDashboardWithSameNameInFolderExists = errors.New("A dashboard with the same name in the folder already exists")
|
|
ErrDashboardVersionMismatch = errors.New("The dashboard has been changed by someone else")
|
|
ErrDashboardTitleEmpty = errors.New("Dashboard title cannot be empty")
|
|
ErrDashboardFolderCannotHaveParent = errors.New("A Dashboard Folder cannot be added to another folder")
|
|
ErrDashboardContainsInvalidAlertData = errors.New("Invalid alert data. Cannot save dashboard")
|
|
ErrDashboardFailedToUpdateAlertData = errors.New("Failed to save alert data")
|
|
ErrDashboardsWithSameSlugExists = errors.New("Multiple dashboards with the same slug exists")
|
|
ErrDashboardFailedGenerateUniqueUid = errors.New("Failed to generate unique dashboard id")
|
|
ErrDashboardTypeMismatch = errors.New("Dashboard cannot be changed to a folder")
|
|
ErrDashboardFolderWithSameNameAsDashboard = errors.New("Folder name cannot be the same as one of its dashboards")
|
|
ErrDashboardWithSameNameAsFolder = errors.New("Dashboard name cannot be the same as folder")
|
|
ErrDashboardFolderNameExists = errors.New("A folder with that name already exists")
|
|
ErrDashboardUpdateAccessDenied = errors.New("Access denied to save dashboard")
|
|
ErrDashboardInvalidUid = errors.New("uid contains illegal characters")
|
|
ErrDashboardUidToLong = errors.New("uid to long. max 40 characters")
|
|
RootFolderName = "General"
|
|
)
|
|
|
|
type UpdatePluginDashboardError struct {
|
|
PluginId string
|
|
}
|
|
|
|
func (d UpdatePluginDashboardError) Error() string {
|
|
return "Dashboard belong to plugin"
|
|
}
|
|
|
|
var (
|
|
DashTypeJson = "file"
|
|
DashTypeDB = "db"
|
|
DashTypeScript = "script"
|
|
DashTypeSnapshot = "snapshot"
|
|
)
|
|
|
|
// Dashboard model
|
|
type Dashboard struct {
|
|
Id int64
|
|
Uid string
|
|
Slug string
|
|
OrgId int64
|
|
GnetId int64
|
|
Version int
|
|
PluginId string
|
|
|
|
Created time.Time
|
|
Updated time.Time
|
|
|
|
UpdatedBy int64
|
|
CreatedBy int64
|
|
FolderId int64
|
|
IsFolder bool
|
|
HasAcl bool
|
|
|
|
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)
|
|
}
|
|
|
|
// GetDashboardIdForSavePermissionCheck return the dashboard id to be used for checking permission of dashboard
|
|
func (d *Dashboard) GetDashboardIdForSavePermissionCheck() int64 {
|
|
if d.Id == 0 {
|
|
return d.FolderId
|
|
}
|
|
|
|
return d.Id
|
|
}
|
|
|
|
// 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.Data.Set("schemaVersion", 16)
|
|
folder.Data.Set("editable", true)
|
|
folder.Data.Set("hideControls", true)
|
|
folder.IsFolder = true
|
|
return folder
|
|
}
|
|
|
|
// GetTags turns the tags in data json into go string array
|
|
func (dash *Dashboard) GetTags() []string {
|
|
return dash.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 savable model
|
|
func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
|
|
dash := NewDashboardFromJson(cmd.Dashboard)
|
|
userId := cmd.UserId
|
|
|
|
if userId == 0 {
|
|
userId = -1
|
|
}
|
|
|
|
if dash.Data.Get("version").MustInt(0) == 0 {
|
|
dash.CreatedBy = userId
|
|
}
|
|
|
|
dash.UpdatedBy = userId
|
|
dash.OrgId = cmd.OrgId
|
|
dash.PluginId = cmd.PluginId
|
|
dash.IsFolder = cmd.IsFolder
|
|
dash.FolderId = cmd.FolderId
|
|
dash.UpdateSlug()
|
|
return dash
|
|
}
|
|
|
|
// GetString a
|
|
func (dash *Dashboard) GetString(prop string, defaultValue string) string {
|
|
return dash.Data.Get(prop).MustString(defaultValue)
|
|
}
|
|
|
|
// UpdateSlug updates the slug
|
|
func (dash *Dashboard) UpdateSlug() {
|
|
title := dash.Data.Get("title").MustString()
|
|
dash.Slug = SlugifyTitle(title)
|
|
}
|
|
|
|
func SlugifyTitle(title string) string {
|
|
return slug.Make(strings.ToLower(title))
|
|
}
|
|
|
|
// GetUrl return the html url for a folder if it's folder, otherwise for a dashboard
|
|
func (dash *Dashboard) GetUrl() string {
|
|
return GetDashboardFolderUrl(dash.IsFolder, dash.Uid, dash.Slug)
|
|
}
|
|
|
|
// Return the html url for a dashboard
|
|
func (dash *Dashboard) GenerateUrl() string {
|
|
return GetDashboardUrl(dash.Uid, dash.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)
|
|
}
|
|
|
|
// Return the html url for a dashboard
|
|
func GetDashboardUrl(uid string, slug string) string {
|
|
return fmt.Sprintf("%s/d/%s/%s", setting.AppSubUrl, uid, slug)
|
|
}
|
|
|
|
// Return the full url for a dashboard
|
|
func GetFullDashboardUrl(uid string, slug string) string {
|
|
return fmt.Sprintf("%s%s", setting.AppUrl, GetDashboardUrl(uid, slug))
|
|
}
|
|
|
|
// GetFolderUrl return 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)
|
|
}
|
|
|
|
//
|
|
// COMMANDS
|
|
//
|
|
|
|
type SaveDashboardCommand struct {
|
|
Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
|
|
UserId int64 `json:"userId"`
|
|
Overwrite bool `json:"overwrite"`
|
|
Message string `json:"message"`
|
|
OrgId int64 `json:"-"`
|
|
RestoredFrom int `json:"-"`
|
|
PluginId string `json:"-"`
|
|
FolderId int64 `json:"folderId"`
|
|
IsFolder bool `json:"isFolder"`
|
|
|
|
UpdatedAt time.Time
|
|
|
|
Result *Dashboard
|
|
}
|
|
|
|
type DashboardProvisioning struct {
|
|
Id int64
|
|
DashboardId int64
|
|
Name string
|
|
ExternalId string
|
|
Updated int64
|
|
}
|
|
|
|
type SaveProvisionedDashboardCommand struct {
|
|
DashboardCmd *SaveDashboardCommand
|
|
DashboardProvisioning *DashboardProvisioning
|
|
|
|
Result *Dashboard
|
|
}
|
|
|
|
type DeleteDashboardCommand struct {
|
|
Id int64
|
|
OrgId int64
|
|
}
|
|
|
|
type ValidateDashboardBeforeSaveCommand struct {
|
|
OrgId int64
|
|
Dashboard *Dashboard
|
|
Overwrite bool
|
|
}
|
|
|
|
//
|
|
// QUERIES
|
|
//
|
|
|
|
type GetDashboardQuery struct {
|
|
Slug string // required if no Id or Uid is specified
|
|
Id int64 // optional if slug is set
|
|
Uid string // optional if slug is set
|
|
OrgId int64
|
|
|
|
Result *Dashboard
|
|
}
|
|
|
|
type DashboardTagCloudItem struct {
|
|
Term string `json:"term"`
|
|
Count int `json:"count"`
|
|
}
|
|
|
|
type GetDashboardTagsQuery struct {
|
|
OrgId int64
|
|
Result []*DashboardTagCloudItem
|
|
}
|
|
|
|
type GetDashboardsQuery struct {
|
|
DashboardIds []int64
|
|
Result []*Dashboard
|
|
}
|
|
|
|
type GetDashboardPermissionsForUserQuery struct {
|
|
DashboardIds []int64
|
|
OrgId int64
|
|
UserId int64
|
|
OrgRole RoleType
|
|
Result []*DashboardPermissionForUser
|
|
}
|
|
|
|
type GetDashboardsByPluginIdQuery struct {
|
|
OrgId int64
|
|
PluginId string
|
|
Result []*Dashboard
|
|
}
|
|
|
|
type GetDashboardSlugByIdQuery struct {
|
|
Id int64
|
|
Result string
|
|
}
|
|
|
|
type GetProvisionedDashboardDataQuery struct {
|
|
Name string
|
|
|
|
Result []*DashboardProvisioning
|
|
}
|
|
|
|
type GetDashboardsBySlugQuery struct {
|
|
OrgId int64
|
|
Slug string
|
|
|
|
Result []*Dashboard
|
|
}
|
|
|
|
type DashboardPermissionForUser struct {
|
|
DashboardId int64 `json:"dashboardId"`
|
|
Permission PermissionType `json:"permission"`
|
|
PermissionName string `json:"permissionName"`
|
|
}
|
|
|
|
type DashboardRef struct {
|
|
Uid string
|
|
Slug string
|
|
}
|
|
|
|
type GetDashboardRefByIdQuery struct {
|
|
Id int64
|
|
Result *DashboardRef
|
|
}
|