Browse Source

feat(plugins): a lot of work on #4298

Torkel Ödegaard 9 years ago
parent
commit
dfaa6d8eb9

+ 0 - 1
pkg/api/api.go

@@ -171,7 +171,6 @@ func Register(r *macaron.Macaron) {
 			r.Put("/:id", bind(m.UpdateDataSourceCommand{}), UpdateDataSource)
 			r.Delete("/:id", DeleteDataSource)
 			r.Get("/:id", wrap(GetDataSourceById))
-			r.Get("/plugins", GetDataSourcePlugins)
 		}, reqOrgAdmin)
 
 		r.Group("/datasources/name/:name", func() {

+ 0 - 19
pkg/api/datasources.go

@@ -6,7 +6,6 @@ import (
 	//"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/plugins"
 	"github.com/grafana/grafana/pkg/util"
 )
 
@@ -100,24 +99,6 @@ func UpdateDataSource(c *middleware.Context, cmd m.UpdateDataSourceCommand) {
 	c.JsonOK("Datasource updated")
 }
 
-func GetDataSourcePlugins(c *middleware.Context) {
-	dsList := make(map[string]*plugins.DataSourcePlugin)
-
-	if enabledPlugins, err := plugins.GetEnabledPlugins(c.OrgId); err != nil {
-		c.JsonApiErr(500, "Failed to get org apps", err)
-		return
-	} else {
-
-		for key, value := range enabledPlugins.DataSources {
-			if !value.BuiltIn {
-				dsList[key] = value
-			}
-		}
-
-		c.JSON(200, dsList)
-	}
-}
-
 // Get /api/datasources/name/:name
 func GetDataSourceByName(c *middleware.Context) Response {
 	query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}

+ 8 - 8
pkg/api/dtos/plugins.go

@@ -5,23 +5,23 @@ import "github.com/grafana/grafana/pkg/plugins"
 type PluginSetting struct {
 	Name         string                      `json:"name"`
 	Type         string                      `json:"type"`
-	PluginId     string                      `json:"pluginId"`
+	Id           string                      `json:"id"`
 	Enabled      bool                        `json:"enabled"`
 	Pinned       bool                        `json:"pinned"`
 	Module       string                      `json:"module"`
 	BaseUrl      string                      `json:"baseUrl"`
 	Info         *plugins.PluginInfo         `json:"info"`
 	Pages        []*plugins.AppPluginPage    `json:"pages"`
-	Includes     []*plugins.AppIncludeInfo   `json:"includes"`
+	Includes     []*plugins.PluginInclude    `json:"includes"`
 	Dependencies *plugins.PluginDependencies `json:"dependencies"`
 	JsonData     map[string]interface{}      `json:"jsonData"`
 }
 
 type PluginListItem struct {
-	Name     string              `json:"name"`
-	Type     string              `json:"type"`
-	PluginId string              `json:"pluginId"`
-	Enabled  bool                `json:"enabled"`
-	Pinned   bool                `json:"pinned"`
-	Info     *plugins.PluginInfo `json:"info"`
+	Name    string              `json:"name"`
+	Type    string              `json:"type"`
+	Id      string              `json:"id"`
+	Enabled bool                `json:"enabled"`
+	Pinned  bool                `json:"pinned"`
+	Info    *plugins.PluginInfo `json:"info"`
 }

+ 25 - 10
pkg/api/plugin_setting.go

@@ -9,6 +9,10 @@ import (
 )
 
 func GetPluginList(c *middleware.Context) Response {
+	typeFilter := c.Query("type")
+	enabledFilter := c.Query("enabled")
+	embeddedFilter := c.Query("embedded")
+
 	pluginSettingsMap, err := plugins.GetPluginSettings(c.OrgId)
 
 	if err != nil {
@@ -17,16 +21,21 @@ func GetPluginList(c *middleware.Context) Response {
 
 	result := make([]*dtos.PluginListItem, 0)
 	for _, pluginDef := range plugins.Plugins {
-		// filter out plugin components
-		if pluginDef.IncludedInAppId != "" {
+		// filter out app sub plugins
+		if embeddedFilter == "0" && pluginDef.IncludedInAppId != "" {
+			continue
+		}
+
+		// filter on type
+		if typeFilter != "" && typeFilter != pluginDef.Type {
 			continue
 		}
 
 		listItem := &dtos.PluginListItem{
-			PluginId: pluginDef.Id,
-			Name:     pluginDef.Name,
-			Type:     pluginDef.Type,
-			Info:     &pluginDef.Info,
+			Id:   pluginDef.Id,
+			Name: pluginDef.Name,
+			Type: pluginDef.Type,
+			Info: &pluginDef.Info,
 		}
 
 		if pluginSetting, exists := pluginSettingsMap[pluginDef.Id]; exists {
@@ -34,6 +43,11 @@ func GetPluginList(c *middleware.Context) Response {
 			listItem.Pinned = pluginSetting.Pinned
 		}
 
+		// filter out disabled
+		if enabledFilter == "1" && !listItem.Enabled {
+			continue
+		}
+
 		result = append(result, listItem)
 	}
 
@@ -46,19 +60,20 @@ func GetPluginSettingById(c *middleware.Context) Response {
 	if def, exists := plugins.Plugins[pluginId]; !exists {
 		return ApiError(404, "Plugin not found, no installed plugin with that id", nil)
 	} else {
+
 		dto := &dtos.PluginSetting{
 			Type:         def.Type,
-			PluginId:     def.Id,
+			Id:           def.Id,
 			Name:         def.Name,
 			Info:         &def.Info,
 			Dependencies: &def.Dependencies,
+			Includes:     def.Includes,
+			BaseUrl:      def.BaseUrl,
+			Module:       def.Module,
 		}
 
 		if app, exists := plugins.Apps[pluginId]; exists {
 			dto.Pages = app.Pages
-			dto.Includes = app.Includes
-			dto.BaseUrl = app.BaseUrl
-			dto.Module = app.Module
 		}
 
 		query := m.GetPluginSettingByIdQuery{PluginId: pluginId, OrgId: c.OrgId}

+ 8 - 1
pkg/models/plugin_setting.go

@@ -61,7 +61,14 @@ func (cmd *UpdatePluginSettingCmd) GetEncryptedJsonData() SecureJsonData {
 // QUERIES
 type GetPluginSettingsQuery struct {
 	OrgId  int64
-	Result []*PluginSetting
+	Result []*PluginSettingInfoDTO
+}
+
+type PluginSettingInfoDTO struct {
+	OrgId    int64
+	PluginId string
+	Enabled  bool
+	Pinned   bool
 }
 
 type GetPluginSettingByIdQuery struct {

+ 6 - 13
pkg/plugins/app_plugin.go

@@ -21,20 +21,13 @@ type AppPluginCss struct {
 	Dark  string `json:"dark"`
 }
 
-type AppIncludeInfo struct {
-	Name string `json:"name"`
-	Type string `json:"type"`
-	Id   string `json:"id"`
-}
-
 type AppPlugin struct {
 	FrontendPluginBase
-	Pages    []*AppPluginPage  `json:"pages"`
-	Routes   []*AppPluginRoute `json:"routes"`
-	Includes []*AppIncludeInfo `json:"-"`
+	Pages  []*AppPluginPage  `json:"pages"`
+	Routes []*AppPluginRoute `json:"routes"`
 
-	Pinned  bool `json:"-"`
-	Enabled bool `json:"-"`
+	FoundChildPlugins []*PluginInclude `json:"-"`
+	Pinned            bool             `json:"-"`
 }
 
 type AppPluginRoute struct {
@@ -71,7 +64,7 @@ func (app *AppPlugin) initApp() {
 	for _, panel := range Panels {
 		if strings.HasPrefix(panel.PluginDir, app.PluginDir) {
 			panel.setPathsBasedOnApp(app)
-			app.Includes = append(app.Includes, &AppIncludeInfo{
+			app.FoundChildPlugins = append(app.FoundChildPlugins, &PluginInclude{
 				Name: panel.Name,
 				Id:   panel.Id,
 				Type: panel.Type,
@@ -83,7 +76,7 @@ func (app *AppPlugin) initApp() {
 	for _, ds := range DataSources {
 		if strings.HasPrefix(ds.PluginDir, app.PluginDir) {
 			ds.setPathsBasedOnApp(app)
-			app.Includes = append(app.Includes, &AppIncludeInfo{
+			app.FoundChildPlugins = append(app.FoundChildPlugins, &PluginInclude{
 				Name: ds.Name,
 				Id:   ds.Id,
 				Type: ds.Type,

+ 5 - 6
pkg/plugins/datasource_plugin.go

@@ -4,12 +4,11 @@ import "encoding/json"
 
 type DataSourcePlugin struct {
 	FrontendPluginBase
-	DefaultMatchFormat string `json:"defaultMatchFormat"`
-	Annotations        bool   `json:"annotations"`
-	Metrics            bool   `json:"metrics"`
-	BuiltIn            bool   `json:"builtIn"`
-	Mixed              bool   `json:"mixed"`
-	App                string `json:"app"`
+	Annotations bool   `json:"annotations"`
+	Metrics     bool   `json:"metrics"`
+	BuiltIn     bool   `json:"builtIn"`
+	Mixed       bool   `json:"mixed"`
+	App         string `json:"app"`
 }
 
 func (p *DataSourcePlugin) Load(decoder *json.Decoder, pluginDir string) error {

+ 0 - 4
pkg/plugins/frontend_plugin.go

@@ -11,10 +11,6 @@ import (
 
 type FrontendPluginBase struct {
 	PluginBase
-	Module        string `json:"module"`
-	BaseUrl       string `json:"baseUrl"`
-	StaticRoot    string `json:"staticRoot"`
-	StaticRootAbs string `json:"-"`
 }
 
 func (fp *FrontendPluginBase) initFrontendPlugin() {

+ 17 - 5
pkg/plugins/models.go

@@ -14,11 +14,16 @@ type PluginLoader interface {
 }
 
 type PluginBase struct {
-	Type         string             `json:"type"`
-	Name         string             `json:"name"`
-	Id           string             `json:"id"`
-	Info         PluginInfo         `json:"info"`
-	Dependencies PluginDependencies `json:"dependencies"`
+	Type          string             `json:"type"`
+	Name          string             `json:"name"`
+	Id            string             `json:"id"`
+	Info          PluginInfo         `json:"info"`
+	Dependencies  PluginDependencies `json:"dependencies"`
+	Includes      []*PluginInclude   `json:"includes"`
+	Module        string             `json:"module"`
+	BaseUrl       string             `json:"baseUrl"`
+	StaticRoot    string             `json:"staticRoot"`
+	StaticRootAbs string             `json:"-"`
 
 	IncludedInAppId string `json:"-"`
 	PluginDir       string `json:"-"`
@@ -51,6 +56,13 @@ type PluginDependencies struct {
 	Plugins        []PluginDependencyItem `json:"plugins"`
 }
 
+type PluginInclude struct {
+	Name string `json:"name"`
+	Path string `json:"path"`
+	Type string `json:"type"`
+	Id   string `json:"id"`
+}
+
 type PluginDependencyItem struct {
 	Type    string `json:"type"`
 	Id      string `json:"id"`

+ 32 - 22
pkg/plugins/queries.go

@@ -5,61 +5,71 @@ import (
 	m "github.com/grafana/grafana/pkg/models"
 )
 
-func GetPluginSettings(orgId int64) (map[string]*m.PluginSetting, error) {
+func GetPluginSettings(orgId int64) (map[string]*m.PluginSettingInfoDTO, error) {
 	query := m.GetPluginSettingsQuery{OrgId: orgId}
 
 	if err := bus.Dispatch(&query); err != nil {
 		return nil, err
 	}
 
-	pluginMap := make(map[string]*m.PluginSetting)
+	pluginMap := make(map[string]*m.PluginSettingInfoDTO)
 	for _, plug := range query.Result {
 		pluginMap[plug.PluginId] = plug
 	}
 
+	for _, pluginDef := range Plugins {
+		// ignore entries that exists
+		if _, ok := pluginMap[pluginDef.Id]; ok {
+			continue
+		}
+
+		// default to enabled true
+		opt := &m.PluginSettingInfoDTO{Enabled: true}
+
+		// if it's included in app check app settings
+		if pluginDef.IncludedInAppId != "" {
+			// app componets are by default disabled
+			opt.Enabled = false
+
+			if appSettings, ok := pluginMap[pluginDef.IncludedInAppId]; ok {
+				opt.Enabled = appSettings.Enabled
+			}
+		}
+
+		pluginMap[pluginDef.Id] = opt
+	}
+
 	return pluginMap, nil
 }
 
 func GetEnabledPlugins(orgId int64) (*EnabledPlugins, error) {
 	enabledPlugins := NewEnabledPlugins()
-	orgPlugins, err := GetPluginSettings(orgId)
+	pluginSettingMap, err := GetPluginSettings(orgId)
 	if err != nil {
 		return nil, err
 	}
 
-	enabledApps := make(map[string]bool)
+	isPluginEnabled := func(pluginId string) bool {
+		_, ok := pluginSettingMap[pluginId]
+		return ok
+	}
 
 	for pluginId, app := range Apps {
-
-		if b, ok := orgPlugins[pluginId]; ok {
-			app.Enabled = b.Enabled
+		if b, ok := pluginSettingMap[pluginId]; ok {
 			app.Pinned = b.Pinned
-		}
-
-		if app.Enabled {
-			enabledApps[pluginId] = true
 			enabledPlugins.Apps = append(enabledPlugins.Apps, app)
 		}
 	}
 
-	isPluginEnabled := func(appId string) bool {
-		if appId == "" {
-			return true
-		}
-
-		_, ok := enabledApps[appId]
-		return ok
-	}
-
 	// add all plugins that are not part of an App.
 	for dsId, ds := range DataSources {
-		if isPluginEnabled(ds.IncludedInAppId) {
+		if isPluginEnabled(ds.Id) {
 			enabledPlugins.DataSources[dsId] = ds
 		}
 	}
 
 	for _, panel := range Panels {
-		if isPluginEnabled(panel.IncludedInAppId) {
+		if isPluginEnabled(panel.Id) {
 			enabledPlugins.Panels = append(enabledPlugins.Panels, panel)
 		}
 	}

+ 5 - 2
pkg/services/sqlstore/plugin_setting.go

@@ -16,9 +16,12 @@ func init() {
 }
 
 func GetPluginSettings(query *m.GetPluginSettingsQuery) error {
-	sess := x.Where("org_id=?", query.OrgId)
+	sql := `SELECT org_id, plugin_id, enabled, pinned
+					FROM plugin_setting
+					WHERE org_id=?`
 
-	query.Result = make([]*m.PluginSetting, 0)
+	sess := x.Sql(sql, query.OrgId)
+	query.Result = make([]*m.PluginSettingInfoDTO, 0)
 	return sess.Find(&query.Result)
 }
 

+ 4 - 1
public/app/features/dashboard/import_list/import_list.ts

@@ -26,7 +26,10 @@ export class DashImportListCtrl {
 }
 
 var template = `
-<button class="btn btn-mini btn-inverse" ng-click="ctrl.import(dash)">Import</span>
+<h3 class="page-heading">Dashboards</h3>
+<div class="gf-form-group">
+  <button class="btn btn-mini btn-inverse" ng-click="ctrl.import(dash)">Import</button>
+</div>
 `;
 
 export function dashboardImportList() {

+ 4 - 2
public/app/features/datasources/edit_ctrl.js

@@ -40,7 +40,7 @@ function (angular, _, config) {
         return $q.when(null);
       }
 
-      return backendSrv.get('/api/datasources/plugins').then(function(plugins) {
+      return backendSrv.get('/api/org/plugins', {enabled: 1, type: 'datasource'}).then(function(plugins) {
         datasourceTypes = plugins;
         $scope.types = plugins;
       });
@@ -55,7 +55,9 @@ function (angular, _, config) {
     };
 
     $scope.typeChanged = function() {
-      $scope.datasourceMeta = $scope.types[$scope.current.type];
+      return backendSrv.get('/api/org/plugins/' + $scope.current.type + '/settings').then(function(pluginInfo) {
+        $scope.datasourceMeta = pluginInfo;
+      });
     };
 
     $scope.updateFrontendSettings = function() {

+ 24 - 22
public/app/features/datasources/partials/edit.html

@@ -27,34 +27,36 @@ icon="icon-gf icon-gf-datasources">
 			<div class="gf-form">
 				<span class="gf-form-label width-7">Type</span>
 				<div class="gf-form-select-wrapper">
-					<select class="gf-form-input gf-size-auto" ng-model="current.type" ng-options="k as v.name for (k, v) in types" ng-change="typeChanged()"></select>
+					<select class="gf-form-input gf-size-auto" ng-model="current.type" ng-options="v.id as v.name for v in types" ng-change="typeChanged()"></select>
 				</div>
 			</div>
+		</div>
 
-			<dashboard-import-list plugin="current"></dashboard-import-list>
 
-			<rebuild-on-change property="datasourceMeta.id">
-				<plugin-component type="datasource-config-ctrl">
-				</plugin-component>
-			</rebuild-on-change>
+		<rebuild-on-change property="datasourceMeta.id">
+			<plugin-component type="datasource-config-ctrl">
+			</plugin-component>
+		</rebuild-on-change>
 
-			<div ng-if="testing" style="margin-top: 25px">
-				<h5 ng-show="!testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
-				<h5 ng-show="testing.done">Test results</h5>
-				<div class="alert-{{testing.status}} alert">
-					<div class="alert-title">{{testing.title}}</div>
-					<div ng-bind='testing.message'></div>
-				</div>
-			</div>
-
-			<div class="gf-form-button-row">
-				<button type="submit" class="btn btn-success" ng-show="isNew" ng-click="saveChanges()">Add</button>
-				<button type="submit" class="btn btn-success" ng-show="!isNew" ng-click="saveChanges()">Save</button>
-				<button type="submit" class="btn btn-secondary" ng-show="!isNew" ng-click="saveChanges(true)">
-					Test Connection
-				</button>
-				<a class="btn btn-link" href="datasources">Cancel</a>
+		<div ng-if="testing" style="margin-top: 25px">
+			<h5 ng-show="!testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>
+			<h5 ng-show="testing.done">Test results</h5>
+			<div class="alert-{{testing.status}} alert">
+				<div class="alert-title">{{testing.title}}</div>
+				<div ng-bind='testing.message'></div>
 			</div>
+		</div>
+
+		<dashboard-import-list plugin="current"></dashboard-import-list>
+
+		<div class="gf-form-button-row">
+			<button type="submit" class="btn btn-success" ng-show="isNew" ng-click="saveChanges()">Add</button>
+			<button type="submit" class="btn btn-success" ng-show="!isNew" ng-click="saveChanges()">Save</button>
+			<button type="submit" class="btn btn-secondary" ng-show="!isNew" ng-click="saveChanges(true)">
+				Test Connection
+			</button>
+			<a class="btn btn-link" href="datasources">Cancel</a>
+		</div>
 
 	</form>
 </div>

+ 0 - 2
public/app/features/plugins/edit_ctrl.ts

@@ -72,8 +72,6 @@ export class PluginEditCtrl {
     // Perform the core update procedure
     chain = chain.then(function() {
       var updateCmd = _.extend({
-        pluginId: self.model.pluginId,
-        orgId: self.model.orgId,
         enabled: self.model.enabled,
         pinned: self.model.pinned,
         jsonData: self.model.jsonData,

+ 2 - 2
public/app/features/plugins/partials/edit.html

@@ -48,7 +48,7 @@
         </div>
       </div>
 
-      <div ng-if="ctrl.model.pluginId">
+      <div ng-if="ctrl.model.id">
         <plugin-component type="app-config-ctrl"></plugin-component>
         <div class="clearfix"></div>
         <button type="submit" class="btn btn-success" ng-click="ctrl.update()">Save</button>
@@ -70,7 +70,7 @@
           </li>
           <li ng-repeat="page in ctrl.model.pages" class="plugin-info-list-item">
             <i class="icon-gf icon-gf-share"></i>
-            <a href="plugins/{{ctrl.pluginId}}/page/{{page.slug}}">{{page.name}}</a>
+            <a href="plugins/{{ctrl.model.id}}/page/{{page.slug}}">{{page.name}}</a>
           </li>
         </ul>
       </section>

+ 2 - 2
public/app/features/plugins/partials/list.html

@@ -18,7 +18,7 @@
 		<tbody>
 			<tr ng-repeat="plugin in ctrl.plugins">
 				<td>
-					<a href="plugins/{{plugin.pluginId}}/edit">
+					<a href="plugins/{{plugin.id}}/edit">
 						{{plugin.name}}
 					</a>
 				</td>
@@ -30,7 +30,7 @@
 					<span class="label label-info" ng-if="plugin.pinned">Pinned</span>
 				</td>
 				<td class="text-right">
-					<a href="plugins/{{plugin.pluginId}}/edit" class="btn btn-inverse btn-small">
+					<a href="plugins/{{plugin.id}}/edit" class="btn btn-inverse btn-small">
 							<i class="fa fa-edit"></i>
 							Edit
 						</a>

+ 4 - 1
public/app/plugins/datasource/graphite/plugin.json

@@ -3,7 +3,10 @@
   "type": "datasource",
   "id": "graphite",
 
-  "defaultMatchFormat": "glob",
+  "includes": [
+    {"type": "dashboard", "name": "Carbon Stats", "path": "dashboards/carbon_stats.json"}
+  ],
+
   "metrics": true,
   "annotations": true
 }