mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(plugins): WIP on new apps concept
This commit is contained in:
parent
0903d5541b
commit
eacc46da6d
@ -121,7 +121,7 @@ func GetDataSourcePlugins(c *middleware.Context) {
|
||||
orgApps := m.GetAppPluginsQuery{OrgId: c.OrgId}
|
||||
err := bus.Dispatch(&orgApps)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Failed to get org plugin Bundles", err)
|
||||
c.JsonApiErr(500, "Failed to get org apps", err)
|
||||
}
|
||||
enabledPlugins := plugins.GetEnabledPlugins(orgApps.Result)
|
||||
|
||||
|
@ -8,9 +8,9 @@ type IndexViewData struct {
|
||||
GoogleAnalyticsId string
|
||||
GoogleTagManagerId string
|
||||
|
||||
PluginCss []*PluginCss
|
||||
PluginJs []string
|
||||
MainNavLinks []*NavLink
|
||||
PluginCss []*PluginCss
|
||||
PluginModules []string
|
||||
MainNavLinks []*NavLink
|
||||
}
|
||||
|
||||
type PluginCss struct {
|
||||
@ -21,5 +21,5 @@ type PluginCss struct {
|
||||
type NavLink struct {
|
||||
Text string `json:"text"`
|
||||
Icon string `json:"icon"`
|
||||
Href string `json:"href"`
|
||||
Url string `json:"url"`
|
||||
}
|
||||
|
@ -60,40 +60,25 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enabledPlugins := plugins.GetEnabledPlugins(orgApps.Result)
|
||||
|
||||
for _, plugin := range enabledPlugins.AppPlugins {
|
||||
for _, js := range plugin.Js {
|
||||
data.PluginJs = append(data.PluginJs, js.Module)
|
||||
}
|
||||
for _, css := range plugin.Css {
|
||||
data.PluginCss = append(data.PluginCss, &dtos.PluginCss{Light: css.Light, Dark: css.Dark})
|
||||
for _, plugin := range enabledPlugins.Apps {
|
||||
if plugin.Module != "" {
|
||||
data.PluginModules = append(data.PluginModules, plugin.Module)
|
||||
}
|
||||
|
||||
if plugin.PinNavLinks {
|
||||
for _, item := range plugin.MainNavLinks {
|
||||
// only show menu items for the specified roles.
|
||||
var validRoles []m.RoleType
|
||||
if string(item.ReqRole) == "" || item.ReqRole == m.ROLE_VIEWER {
|
||||
validRoles = []m.RoleType{m.ROLE_ADMIN, m.ROLE_EDITOR, m.ROLE_VIEWER}
|
||||
} else if item.ReqRole == m.ROLE_EDITOR {
|
||||
validRoles = []m.RoleType{m.ROLE_ADMIN, m.ROLE_EDITOR}
|
||||
} else if item.ReqRole == m.ROLE_ADMIN {
|
||||
validRoles = []m.RoleType{m.ROLE_ADMIN}
|
||||
}
|
||||
ok := true
|
||||
if len(validRoles) > 0 {
|
||||
ok = false
|
||||
for _, role := range validRoles {
|
||||
if role == c.OrgRole {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{Text: item.Text, Href: item.Href, Icon: item.Icon})
|
||||
}
|
||||
if plugin.Css != nil {
|
||||
data.PluginCss = append(data.PluginCss, &dtos.PluginCss{Light: plugin.Css.Light, Dark: plugin.Css.Dark})
|
||||
}
|
||||
|
||||
if plugin.Pinned && plugin.Page != nil {
|
||||
if c.userHasRole(plugin.Page.reqRole) {
|
||||
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{
|
||||
Text: plugin.Page.Text,
|
||||
Url: plugin.Page.Url,
|
||||
Icon: plugin.Page.Icon,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -253,3 +253,7 @@ func (ctx *Context) JsonApiErr(status int, message string, err error) {
|
||||
|
||||
ctx.JSON(status, resp)
|
||||
}
|
||||
|
||||
func (ctx *Context) hasUserRole(role m.RoleType) bool {
|
||||
return ctx.OrgRole.Includes(role)
|
||||
}
|
||||
|
@ -3,12 +3,12 @@ package models
|
||||
import "time"
|
||||
|
||||
type AppPlugin struct {
|
||||
Id int64
|
||||
Type string
|
||||
OrgId int64
|
||||
Enabled bool
|
||||
PinNavLinks bool
|
||||
JsonData map[string]interface{}
|
||||
Id int64
|
||||
Type string
|
||||
OrgId int64
|
||||
Enabled bool
|
||||
Pinned bool
|
||||
JsonData map[string]interface{}
|
||||
|
||||
Created time.Time
|
||||
Updated time.Time
|
||||
@ -19,10 +19,10 @@ type AppPlugin struct {
|
||||
|
||||
// Also acts as api DTO
|
||||
type UpdateAppPluginCmd struct {
|
||||
Type string `json:"type" binding:"Required"`
|
||||
Enabled bool `json:"enabled"`
|
||||
PinNavLinks bool `json:"pin_nav_links"`
|
||||
JsonData map[string]interface{} `json:"jsonData"`
|
||||
Type string `json:"type" binding:"Required"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Pinned bool `json:"pinned"`
|
||||
JsonData map[string]interface{} `json:"jsonData"`
|
||||
|
||||
Id int64 `json:"-"`
|
||||
OrgId int64 `json:"-"`
|
||||
|
@ -26,6 +26,17 @@ func (r RoleType) IsValid() bool {
|
||||
return r == ROLE_VIEWER || r == ROLE_ADMIN || r == ROLE_EDITOR || r == ROLE_READ_ONLY_EDITOR
|
||||
}
|
||||
|
||||
func (r RoleType) Includes(other RoleType) bool {
|
||||
if r == ROLE_ADMIN {
|
||||
return true
|
||||
}
|
||||
if r == ROLE_EDITOR || r == ROLE_READ_ONLY_EDITOR {
|
||||
return other != ROLE_ADMIN
|
||||
}
|
||||
|
||||
return r == other
|
||||
}
|
||||
|
||||
type OrgUser struct {
|
||||
Id int64
|
||||
OrgId int64
|
||||
|
@ -15,20 +15,20 @@ type DataSourcePlugin struct {
|
||||
Metrics bool `json:"metrics"`
|
||||
BuiltIn bool `json:"builtIn"`
|
||||
App string `json:"app"`
|
||||
StaticRootConfig *StaticRootConfig `json:"staticRoot"`
|
||||
PublicContent *PublicContent `json:"public"`
|
||||
}
|
||||
|
||||
type PanelPlugin struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Module string `json:"module"`
|
||||
StaticRootConfig *StaticRootConfig `json:"staticRoot"`
|
||||
App string `json:"app"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Module string `json:"module"`
|
||||
PublicContent *PublicContent `json:"public"`
|
||||
App string `json:"app"`
|
||||
}
|
||||
|
||||
type StaticRootConfig struct {
|
||||
Url string `json:"url"`
|
||||
Path string `json:"path"`
|
||||
type PublicContent struct {
|
||||
UrlFragment string `json:"urlFragment"`
|
||||
Dir string `json:"dir"`
|
||||
}
|
||||
|
||||
type ApiPluginRoute struct {
|
||||
@ -41,14 +41,10 @@ type ApiPluginRoute struct {
|
||||
App string `json:"app"`
|
||||
}
|
||||
|
||||
type AppPluginJs struct {
|
||||
Module string `json:"module"`
|
||||
}
|
||||
|
||||
type AppPluginNavLink struct {
|
||||
type AppPluginPage struct {
|
||||
Text string `json:"text"`
|
||||
Icon string `json:"icon"`
|
||||
Href string `json:"href"`
|
||||
Href string `json:"url"`
|
||||
ReqRole models.RoleType `json:"reqRole"`
|
||||
}
|
||||
|
||||
@ -64,31 +60,27 @@ type ApiPlugin struct {
|
||||
}
|
||||
|
||||
type AppPlugin struct {
|
||||
Type string `json:"type"`
|
||||
Enabled bool `json:"enabled"`
|
||||
PanelPlugins []string `json:"panelPlugins"`
|
||||
DatasourcePlugins []string `json:"datasourcePlugins"`
|
||||
ApiPlugins []string `json:"apiPlugins"`
|
||||
Module string `json:"module"`
|
||||
Js []*AppPluginJs `json:"js"`
|
||||
Css []*AppPluginCss `json:"css"`
|
||||
MainNavLinks []*AppPluginNavLink `json:"mainNavLinks"`
|
||||
PinNavLinks bool `json:"pinNavLinks"`
|
||||
StaticRootConfig *StaticRootConfig `json:"staticRoot"`
|
||||
Type string `json:"type"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Pinned bool `json:"pinned"`
|
||||
Module string `json:"module"`
|
||||
Css *AppPluginCss `json:"css"`
|
||||
Page *AppPluginPage `json:"page"`
|
||||
PublicContent *PublicContent `json:"public"`
|
||||
}
|
||||
|
||||
type EnabledPlugins struct {
|
||||
PanelPlugins []*PanelPlugin
|
||||
DataSourcePlugins map[string]*DataSourcePlugin
|
||||
ApiPlugins []*ApiPlugin
|
||||
AppPlugins []*AppPlugin
|
||||
Panels []*PanelPlugin
|
||||
DataSources map[string]*DataSourcePlugin
|
||||
ApiList []*ApiPlugin
|
||||
Apps []*AppPlugin
|
||||
}
|
||||
|
||||
func NewEnabledPlugins() EnabledPlugins {
|
||||
return EnabledPlugins{
|
||||
PanelPlugins: make([]*PanelPlugin, 0),
|
||||
DataSourcePlugins: make(map[string]*DataSourcePlugin),
|
||||
ApiPlugins: make([]*ApiPlugin, 0),
|
||||
AppPlugins: make([]*AppPlugin, 0),
|
||||
Panels: make([]*PanelPlugin, 0),
|
||||
DataSources: make(map[string]*DataSourcePlugin),
|
||||
ApiList: make([]*ApiPlugin, 0),
|
||||
Apps: make([]*AppPlugin, 0),
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ var (
|
||||
DataSources map[string]*DataSourcePlugin
|
||||
Panels map[string]*PanelPlugin
|
||||
ApiPlugins map[string]*ApiPlugin
|
||||
StaticRoutes []*StaticRootConfig
|
||||
StaticRoutes []*PublicContent
|
||||
Apps map[string]*AppPlugin
|
||||
)
|
||||
|
||||
@ -30,35 +30,35 @@ type PluginScanner struct {
|
||||
func Init() error {
|
||||
DataSources = make(map[string]*DataSourcePlugin)
|
||||
ApiPlugins = make(map[string]*ApiPlugin)
|
||||
StaticRoutes = make([]*StaticRootConfig, 0)
|
||||
StaticRoutes = make([]*PublicContent, 0)
|
||||
Panels = make(map[string]*PanelPlugin)
|
||||
Apps = make(map[string]*AppPlugin)
|
||||
|
||||
scan(path.Join(setting.StaticRootPath, "app/plugins"))
|
||||
checkPluginPaths()
|
||||
checkDependencies()
|
||||
// checkDependencies()
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkDependencies() {
|
||||
for appType, app := range Apps {
|
||||
for _, reqPanel := range app.PanelPlugins {
|
||||
if _, ok := Panels[reqPanel]; !ok {
|
||||
log.Fatal(4, "App %s requires Panel type %s, but it is not present.", appType, reqPanel)
|
||||
}
|
||||
}
|
||||
for _, reqDataSource := range app.DatasourcePlugins {
|
||||
if _, ok := DataSources[reqDataSource]; !ok {
|
||||
log.Fatal(4, "App %s requires DataSource type %s, but it is not present.", appType, reqDataSource)
|
||||
}
|
||||
}
|
||||
for _, reqApiPlugin := range app.ApiPlugins {
|
||||
if _, ok := ApiPlugins[reqApiPlugin]; !ok {
|
||||
log.Fatal(4, "App %s requires ApiPlugin type %s, but it is not present.", appType, reqApiPlugin)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// func checkDependencies() {
|
||||
// for appType, app := range Apps {
|
||||
// for _, reqPanel := range app.PanelPlugins {
|
||||
// if _, ok := Panels[reqPanel]; !ok {
|
||||
// log.Fatal(4, "App %s requires Panel type %s, but it is not present.", appType, reqPanel)
|
||||
// }
|
||||
// }
|
||||
// for _, reqDataSource := range app.DatasourcePlugins {
|
||||
// if _, ok := DataSources[reqDataSource]; !ok {
|
||||
// log.Fatal(4, "App %s requires DataSource type %s, but it is not present.", appType, reqDataSource)
|
||||
// }
|
||||
// }
|
||||
// for _, reqApiPlugin := range app.ApiPlugins {
|
||||
// if _, ok := ApiPlugins[reqApiPlugin]; !ok {
|
||||
// log.Fatal(4, "App %s requires ApiPlugin type %s, but it is not present.", appType, reqApiPlugin)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
func checkPluginPaths() error {
|
||||
for _, section := range setting.Cfg.Sections() {
|
||||
@ -108,10 +108,10 @@ func (scanner *PluginScanner) walker(currentPath string, f os.FileInfo, err erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func addStaticRoot(staticRootConfig *StaticRootConfig, currentDir string) {
|
||||
if staticRootConfig != nil {
|
||||
staticRootConfig.Path = path.Join(currentDir, staticRootConfig.Path)
|
||||
StaticRoutes = append(StaticRoutes, staticRootConfig)
|
||||
func addPublicContent(public *PublicContent, currentDir string) {
|
||||
if public != nil {
|
||||
public.Dir = path.Join(currentDir, public.Dir)
|
||||
StaticRoutes = append(StaticRoutes, public)
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +148,7 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
|
||||
}
|
||||
|
||||
DataSources[p.Type] = &p
|
||||
addStaticRoot(p.StaticRootConfig, currentDir)
|
||||
addPublicContent(p.PublicContent, currentDir)
|
||||
}
|
||||
|
||||
if pluginType == "panel" {
|
||||
@ -163,7 +163,7 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
|
||||
}
|
||||
|
||||
Panels[p.Type] = &p
|
||||
addStaticRoot(p.StaticRootConfig, currentDir)
|
||||
addPublicContent(p.PublicContent, currentDir)
|
||||
}
|
||||
|
||||
if pluginType == "api" {
|
||||
@ -188,7 +188,7 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
|
||||
return errors.New("Did not find type property in plugin.json")
|
||||
}
|
||||
Apps[p.Type] = &p
|
||||
addStaticRoot(p.StaticRootConfig, currentDir)
|
||||
addPublicContent(p.PublicContent, currentDir)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -212,54 +212,56 @@ func GetEnabledPlugins(orgApps []*models.AppPlugin) EnabledPlugins {
|
||||
// state stored there.
|
||||
if b, ok := orgAppsMap[appType]; ok {
|
||||
app.Enabled = b.Enabled
|
||||
app.PinNavLinks = b.PinNavLinks
|
||||
app.Pinned = b.Pinned
|
||||
}
|
||||
|
||||
if app.Enabled {
|
||||
for _, d := range app.DatasourcePlugins {
|
||||
if ds, ok := DataSources[d]; ok {
|
||||
enabledPlugins.DataSourcePlugins[d] = ds
|
||||
}
|
||||
}
|
||||
for _, p := range app.PanelPlugins {
|
||||
if panel, ok := Panels[p]; ok {
|
||||
if _, ok := seenPanels[p]; !ok {
|
||||
seenPanels[p] = true
|
||||
enabledPlugins.PanelPlugins = append(enabledPlugins.PanelPlugins, panel)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, a := range app.ApiPlugins {
|
||||
if api, ok := ApiPlugins[a]; ok {
|
||||
if _, ok := seenApi[a]; !ok {
|
||||
seenApi[a] = true
|
||||
enabledPlugins.ApiPlugins = append(enabledPlugins.ApiPlugins, api)
|
||||
}
|
||||
}
|
||||
}
|
||||
enabledPlugins.AppPlugins = append(enabledPlugins.AppPlugins, &app)
|
||||
}
|
||||
// if app.Enabled {
|
||||
// for _, d := range app.DatasourcePlugins {
|
||||
// if ds, ok := DataSources[d]; ok {
|
||||
// enabledPlugins.DataSourcePlugins[d] = ds
|
||||
// }
|
||||
// }
|
||||
// for _, p := range app.PanelPlugins {
|
||||
// if panel, ok := Panels[p]; ok {
|
||||
// if _, ok := seenPanels[p]; !ok {
|
||||
// seenPanels[p] = true
|
||||
// enabledPlugins.PanelPlugins = append(enabledPlugins.PanelPlugins, panel)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// for _, a := range app.ApiPlugins {
|
||||
// if api, ok := ApiPlugins[a]; ok {
|
||||
// if _, ok := seenApi[a]; !ok {
|
||||
// seenApi[a] = true
|
||||
// enabledPlugins.ApiPlugins = append(enabledPlugins.ApiPlugins, api)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// enabledPlugins.AppPlugins = append(enabledPlugins.AppPlugins, &app)
|
||||
// }
|
||||
}
|
||||
|
||||
// add all plugins that are not part of an App.
|
||||
for d, installedDs := range DataSources {
|
||||
if installedDs.App == "" {
|
||||
enabledPlugins.DataSourcePlugins[d] = installedDs
|
||||
enabledPlugins.DataSources[d] = installedDs
|
||||
}
|
||||
}
|
||||
|
||||
for p, panel := range Panels {
|
||||
if panel.App == "" {
|
||||
if _, ok := seenPanels[p]; !ok {
|
||||
seenPanels[p] = true
|
||||
enabledPlugins.PanelPlugins = append(enabledPlugins.PanelPlugins, panel)
|
||||
enabledPlugins.Panels = append(enabledPlugins.Panels, panel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for a, api := range ApiPlugins {
|
||||
if api.App == "" {
|
||||
if _, ok := seenApi[a]; !ok {
|
||||
seenApi[a] = true
|
||||
enabledPlugins.ApiPlugins = append(enabledPlugins.ApiPlugins, api)
|
||||
enabledPlugins.ApiList = append(enabledPlugins.ApiList, api)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,23 +25,24 @@ func UpdateAppPlugin(cmd *m.UpdateAppPluginCmd) error {
|
||||
|
||||
exists, err := sess.Where("org_id=? and type=?", cmd.OrgId, cmd.Type).Get(&app)
|
||||
sess.UseBool("enabled")
|
||||
sess.UseBool("pin_nav_links")
|
||||
sess.UseBool("pinned")
|
||||
if !exists {
|
||||
app = m.AppPlugin{
|
||||
Type: cmd.Type,
|
||||
OrgId: cmd.OrgId,
|
||||
Enabled: cmd.Enabled,
|
||||
PinNavLinks: cmd.PinNavLinks,
|
||||
JsonData: cmd.JsonData,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
Type: cmd.Type,
|
||||
OrgId: cmd.OrgId,
|
||||
Enabled: cmd.Enabled,
|
||||
Pinned: cmd.Pinned,
|
||||
JsonData: cmd.JsonData,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
_, err = sess.Insert(&app)
|
||||
return err
|
||||
} else {
|
||||
app.Updated = time.Now()
|
||||
app.Enabled = cmd.Enabled
|
||||
app.JsonData = cmd.JsonData
|
||||
app.PinNavLinks = cmd.PinNavLinks
|
||||
app.Pinned = cmd.Pinned
|
||||
_, err = sess.Id(app.Id).Update(&app)
|
||||
return err
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ func addAppPluginMigration(mg *Migrator) {
|
||||
{Name: "org_id", Type: DB_BigInt, Nullable: true},
|
||||
{Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false},
|
||||
{Name: "enabled", Type: DB_Bool, Nullable: false},
|
||||
{Name: "pin_nav_links", Type: DB_Bool, Nullable: false},
|
||||
{Name: "pinned", Type: DB_Bool, Nullable: false},
|
||||
{Name: "json_data", Type: DB_Text, Nullable: true},
|
||||
{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||
{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||
|
Loading…
Reference in New Issue
Block a user