mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
add pluginBundle backend api methods and SQL storage
This commit is contained in:
parent
bd4cb549d6
commit
c4a0fe0234
@ -13,7 +13,7 @@ func Register(r *macaron.Macaron) {
|
|||||||
reqSignedIn := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true})
|
reqSignedIn := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true})
|
||||||
reqGrafanaAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true})
|
reqGrafanaAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true})
|
||||||
reqEditorRole := middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN)
|
reqEditorRole := middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN)
|
||||||
regOrgAdmin := middleware.RoleAuth(m.ROLE_ADMIN)
|
reqOrgAdmin := middleware.RoleAuth(m.ROLE_ADMIN)
|
||||||
quota := middleware.Quota
|
quota := middleware.Quota
|
||||||
bind := binding.Bind
|
bind := binding.Bind
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ func Register(r *macaron.Macaron) {
|
|||||||
r.Get("/invites", wrap(GetPendingOrgInvites))
|
r.Get("/invites", wrap(GetPendingOrgInvites))
|
||||||
r.Post("/invites", quota("user"), bind(dtos.AddInviteForm{}), wrap(AddOrgInvite))
|
r.Post("/invites", quota("user"), bind(dtos.AddInviteForm{}), wrap(AddOrgInvite))
|
||||||
r.Patch("/invites/:code/revoke", wrap(RevokeInvite))
|
r.Patch("/invites/:code/revoke", wrap(RevokeInvite))
|
||||||
}, regOrgAdmin)
|
}, reqOrgAdmin)
|
||||||
|
|
||||||
// create new org
|
// create new org
|
||||||
r.Post("/orgs", quota("org"), bind(m.CreateOrgCommand{}), wrap(CreateOrg))
|
r.Post("/orgs", quota("org"), bind(m.CreateOrgCommand{}), wrap(CreateOrg))
|
||||||
@ -140,7 +140,7 @@ func Register(r *macaron.Macaron) {
|
|||||||
r.Get("/", wrap(GetApiKeys))
|
r.Get("/", wrap(GetApiKeys))
|
||||||
r.Post("/", quota("api_key"), bind(m.AddApiKeyCommand{}), wrap(AddApiKey))
|
r.Post("/", quota("api_key"), bind(m.AddApiKeyCommand{}), wrap(AddApiKey))
|
||||||
r.Delete("/:id", wrap(DeleteApiKey))
|
r.Delete("/:id", wrap(DeleteApiKey))
|
||||||
}, regOrgAdmin)
|
}, reqOrgAdmin)
|
||||||
|
|
||||||
// Data sources
|
// Data sources
|
||||||
r.Group("/datasources", func() {
|
r.Group("/datasources", func() {
|
||||||
@ -150,7 +150,13 @@ func Register(r *macaron.Macaron) {
|
|||||||
r.Delete("/:id", DeleteDataSource)
|
r.Delete("/:id", DeleteDataSource)
|
||||||
r.Get("/:id", wrap(GetDataSourceById))
|
r.Get("/:id", wrap(GetDataSourceById))
|
||||||
r.Get("/plugins", GetDataSourcePlugins)
|
r.Get("/plugins", GetDataSourcePlugins)
|
||||||
}, regOrgAdmin)
|
}, reqOrgAdmin)
|
||||||
|
|
||||||
|
// PluginBundles
|
||||||
|
r.Group("/plugins", func() {
|
||||||
|
r.Get("/", wrap(GetPluginBundles))
|
||||||
|
r.Post("/", bind(m.UpdatePluginBundleCmd{}), wrap(UpdatePluginBundle))
|
||||||
|
}, reqOrgAdmin)
|
||||||
|
|
||||||
r.Get("/frontend/settings/", GetFrontendSettings)
|
r.Get("/frontend/settings/", GetFrontendSettings)
|
||||||
r.Any("/datasources/proxy/:id/*", reqSignedIn, ProxyDataSourceRequest)
|
r.Any("/datasources/proxy/:id/*", reqSignedIn, ProxyDataSourceRequest)
|
||||||
|
@ -115,9 +115,13 @@ func UpdateDataSource(c *middleware.Context, cmd m.UpdateDataSourceCommand) {
|
|||||||
|
|
||||||
func GetDataSourcePlugins(c *middleware.Context) {
|
func GetDataSourcePlugins(c *middleware.Context) {
|
||||||
dsList := make(map[string]interface{})
|
dsList := make(map[string]interface{})
|
||||||
//TODO(awoods): query DB for orgPlugins
|
|
||||||
orgPlugins := map[string]m.PluginBundle{}
|
orgBundles := m.GetPluginBundlesQuery{OrgId: c.OrgId}
|
||||||
enabledPlugins := plugins.GetEnabledPlugins(orgPlugins)
|
err := bus.Dispatch(&orgBundles)
|
||||||
|
if err != nil {
|
||||||
|
c.JsonApiErr(500, "Failed to get org plugin Bundles", err)
|
||||||
|
}
|
||||||
|
enabledPlugins := plugins.GetEnabledPlugins(orgBundles.Result)
|
||||||
|
|
||||||
for key, value := range enabledPlugins.DataSourcePlugins {
|
for key, value := range enabledPlugins.DataSourcePlugins {
|
||||||
if !value.BuiltIn {
|
if !value.BuiltIn {
|
||||||
|
8
pkg/api/dtos/plugin_bundle.go
Normal file
8
pkg/api/dtos/plugin_bundle.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package dtos
|
||||||
|
|
||||||
|
type PluginBundle struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Module string `json:"module"`
|
||||||
|
JsonData map[string]interface{} `json:"jsonData"`
|
||||||
|
}
|
@ -29,9 +29,12 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
|
|||||||
datasources := make(map[string]interface{})
|
datasources := make(map[string]interface{})
|
||||||
var defaultDatasource string
|
var defaultDatasource string
|
||||||
|
|
||||||
//TODO(awoods): query DB to get list of the users plugin preferences.
|
orgBundles := m.GetPluginBundlesQuery{OrgId: c.OrgId}
|
||||||
orgPlugins := map[string]m.PluginBundle{}
|
err := bus.Dispatch(&orgBundles)
|
||||||
enabledPlugins := plugins.GetEnabledPlugins(orgPlugins)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
enabledPlugins := plugins.GetEnabledPlugins(orgBundles.Result)
|
||||||
|
|
||||||
for _, ds := range orgDataSources {
|
for _, ds := range orgDataSources {
|
||||||
url := ds.Url
|
url := ds.Url
|
||||||
|
@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
"github.com/grafana/grafana/pkg/middleware"
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
m "github.com/grafana/grafana/pkg/models"
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
@ -62,9 +63,13 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO(awoods): query DB to get list of the users plugin preferences.
|
orgBundles := m.GetPluginBundlesQuery{OrgId: c.OrgId}
|
||||||
orgPlugins := map[string]m.PluginBundle{}
|
err = bus.Dispatch(&orgBundles)
|
||||||
enabledPlugins := plugins.GetEnabledPlugins(orgPlugins)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
enabledPlugins := plugins.GetEnabledPlugins(orgBundles.Result)
|
||||||
|
|
||||||
for _, plugin := range enabledPlugins.ExternalPlugins {
|
for _, plugin := range enabledPlugins.ExternalPlugins {
|
||||||
for _, js := range plugin.Js {
|
for _, js := range plugin.Js {
|
||||||
data.PluginJs = append(data.PluginJs, js.Module)
|
data.PluginJs = append(data.PluginJs, js.Module)
|
||||||
|
65
pkg/api/plugin_bundle.go
Normal file
65
pkg/api/plugin_bundle.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
"github.com/grafana/grafana/pkg/middleware"
|
||||||
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPluginBundles(c *middleware.Context) Response {
|
||||||
|
query := m.GetPluginBundlesQuery{OrgId: c.OrgId}
|
||||||
|
|
||||||
|
if err := bus.Dispatch(&query); err != nil {
|
||||||
|
return ApiError(500, "Failed to list Plugin Bundles", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
installedBundlesMap := make(map[string]*dtos.PluginBundle)
|
||||||
|
for t, b := range plugins.Bundles {
|
||||||
|
installedBundlesMap[t] = &dtos.PluginBundle{
|
||||||
|
Type: b.Type,
|
||||||
|
Enabled: b.Enabled,
|
||||||
|
Module: b.Module,
|
||||||
|
JsonData: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
seenBundles := make(map[string]bool)
|
||||||
|
|
||||||
|
result := make([]*dtos.PluginBundle, 0)
|
||||||
|
for _, b := range query.Result {
|
||||||
|
if def, ok := installedBundlesMap[b.Type]; ok {
|
||||||
|
result = append(result, &dtos.PluginBundle{
|
||||||
|
Type: b.Type,
|
||||||
|
Enabled: b.Enabled,
|
||||||
|
Module: def.Module,
|
||||||
|
JsonData: b.JsonData,
|
||||||
|
})
|
||||||
|
seenBundles[b.Type] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for t, b := range installedBundlesMap {
|
||||||
|
if _, ok := seenBundles[t]; !ok {
|
||||||
|
result = append(result, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(200, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdatePluginBundle(c *middleware.Context, cmd m.UpdatePluginBundleCmd) Response {
|
||||||
|
cmd.OrgId = c.OrgId
|
||||||
|
|
||||||
|
if _, ok := plugins.Bundles[cmd.Type]; !ok {
|
||||||
|
return ApiError(404, "Bundle type not installed.", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := bus.Dispatch(&cmd)
|
||||||
|
if err != nil {
|
||||||
|
return ApiError(500, "Failed to update plugin bundle", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiSuccess("Plugin updated")
|
||||||
|
}
|
@ -1,8 +1,34 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type PluginBundle struct {
|
type PluginBundle struct {
|
||||||
Id int64
|
Id int64
|
||||||
Type string
|
Type string
|
||||||
Org int64
|
OrgId int64
|
||||||
Enabled bool
|
Enabled bool
|
||||||
|
JsonData map[string]interface{}
|
||||||
|
|
||||||
|
Created time.Time
|
||||||
|
Updated time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
// COMMANDS
|
||||||
|
|
||||||
|
// Also acts as api DTO
|
||||||
|
type UpdatePluginBundleCmd struct {
|
||||||
|
Type string `json:"type" binding:"Required"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
JsonData map[string]interface{} `json:"jsonData"`
|
||||||
|
|
||||||
|
Id int64 `json:"-"`
|
||||||
|
OrgId int64 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------
|
||||||
|
// QUERIES
|
||||||
|
type GetPluginBundlesQuery struct {
|
||||||
|
OrgId int64
|
||||||
|
Result []*PluginBundle
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,7 @@ type ExternalPluginRoute struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ExternalPluginJs struct {
|
type ExternalPluginJs struct {
|
||||||
Module string `json:"module"`
|
Module string `json:"module"`
|
||||||
Directive string `json:"Directive"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExternalPluginNavLink struct {
|
type ExternalPluginNavLink struct {
|
||||||
@ -68,6 +67,7 @@ type PluginBundle struct {
|
|||||||
PanelPlugins []string `json:"panelPlugins"`
|
PanelPlugins []string `json:"panelPlugins"`
|
||||||
DatasourcePlugins []string `json:"datasourcePlugins"`
|
DatasourcePlugins []string `json:"datasourcePlugins"`
|
||||||
ExternalPlugins []string `json:"externalPlugins"`
|
ExternalPlugins []string `json:"externalPlugins"`
|
||||||
|
Module string `json:"module"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type EnabledPlugins struct {
|
type EnabledPlugins struct {
|
||||||
|
@ -172,13 +172,18 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetEnabledPlugins(bundles map[string]models.PluginBundle) EnabledPlugins {
|
func GetEnabledPlugins(orgBundles []*models.PluginBundle) EnabledPlugins {
|
||||||
enabledPlugins := NewEnabledPlugins()
|
enabledPlugins := NewEnabledPlugins()
|
||||||
|
|
||||||
|
orgBundlesMap := make(map[string]*models.PluginBundle)
|
||||||
|
for _, orgBundle := range orgBundles {
|
||||||
|
orgBundlesMap[orgBundle.Type] = orgBundle
|
||||||
|
}
|
||||||
|
|
||||||
for bundleType, bundle := range Bundles {
|
for bundleType, bundle := range Bundles {
|
||||||
enabled := bundle.Enabled
|
enabled := bundle.Enabled
|
||||||
// check if the bundle is stored in the DB.
|
// check if the bundle is stored in the DB.
|
||||||
if b, ok := bundles[bundleType]; ok {
|
if b, ok := orgBundlesMap[bundleType]; ok {
|
||||||
enabled = b.Enabled
|
enabled = b.Enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ func AddMigrations(mg *Migrator) {
|
|||||||
addApiKeyMigrations(mg)
|
addApiKeyMigrations(mg)
|
||||||
addDashboardSnapshotMigrations(mg)
|
addDashboardSnapshotMigrations(mg)
|
||||||
addQuotaMigration(mg)
|
addQuotaMigration(mg)
|
||||||
|
addPluginBundleMigration(mg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addMigrationLogMigrations(mg *Migrator) {
|
func addMigrationLogMigrations(mg *Migrator) {
|
||||||
|
26
pkg/services/sqlstore/migrations/plugin_bundle.go
Normal file
26
pkg/services/sqlstore/migrations/plugin_bundle.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
||||||
|
|
||||||
|
func addPluginBundleMigration(mg *Migrator) {
|
||||||
|
|
||||||
|
var pluginBundleV1 = Table{
|
||||||
|
Name: "plugin_bundle",
|
||||||
|
Columns: []*Column{
|
||||||
|
{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
|
||||||
|
{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: "json_data", Type: DB_Text, Nullable: true},
|
||||||
|
{Name: "created", Type: DB_DateTime, Nullable: false},
|
||||||
|
{Name: "updated", Type: DB_DateTime, Nullable: false},
|
||||||
|
},
|
||||||
|
Indices: []*Index{
|
||||||
|
{Cols: []string{"org_id", "type"}, Type: UniqueIndex},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mg.AddMigration("create plugin_bundle table v1", NewAddTableMigration(pluginBundleV1))
|
||||||
|
|
||||||
|
//------- indexes ------------------
|
||||||
|
addTableIndicesMigrations(mg, "v1", pluginBundleV1)
|
||||||
|
}
|
46
pkg/services/sqlstore/plugin_bundle.go
Normal file
46
pkg/services/sqlstore/plugin_bundle.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package sqlstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/bus"
|
||||||
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bus.AddHandler("sql", GetPluginBundles)
|
||||||
|
bus.AddHandler("sql", UpdatePluginBundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPluginBundles(query *m.GetPluginBundlesQuery) error {
|
||||||
|
sess := x.Where("org_id=?", query.OrgId)
|
||||||
|
|
||||||
|
query.Result = make([]*m.PluginBundle, 0)
|
||||||
|
return sess.Find(&query.Result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdatePluginBundle(cmd *m.UpdatePluginBundleCmd) error {
|
||||||
|
return inTransaction2(func(sess *session) error {
|
||||||
|
var bundle m.PluginBundle
|
||||||
|
|
||||||
|
exists, err := sess.Where("org_id=? and type=?", cmd.OrgId, cmd.Type).Get(&bundle)
|
||||||
|
sess.UseBool("enabled")
|
||||||
|
if !exists {
|
||||||
|
bundle = m.PluginBundle{
|
||||||
|
Type: cmd.Type,
|
||||||
|
OrgId: cmd.OrgId,
|
||||||
|
Enabled: cmd.Enabled,
|
||||||
|
JsonData: cmd.JsonData,
|
||||||
|
Created: time.Now(),
|
||||||
|
Updated: time.Now(),
|
||||||
|
}
|
||||||
|
_, err = sess.Insert(&bundle)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
bundle.Enabled = cmd.Enabled
|
||||||
|
bundle.JsonData = cmd.JsonData
|
||||||
|
_, err = sess.Id(bundle.Id).Update(&bundle)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
{
|
{
|
||||||
"pluginType": "bundle",
|
"pluginType": "bundle",
|
||||||
"type": "core",
|
"type": "core",
|
||||||
|
"module": "",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"panelPlugins": ["graph", "singlestat", "text", "dashlist"],
|
"panelPlugins": ["graph", "singlestat", "text", "dashlist", "table"],
|
||||||
"datasourcePlugins": ["grafana", "graphite"],
|
"datasourcePlugins": ["mixed", "grafana", "graphite", "cloudwatch", "elasticsearch", "influxdb", "influxdb_08", "kairosdb", "opentsdb", "prometheus"],
|
||||||
"externalPlugins": []
|
"externalPlugins": []
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user