Commit 60adcede by Torkel Ödegaard

feat(plugins): worked on plugin dashboard import handling

parent 78b9c300
......@@ -31,7 +31,7 @@
"dependencies": {
"babel-plugin-transform-es2015-modules-systemjs": "^6.5.0",
"babel-preset-es2015": "^6.5.0",
"lodash": "~4.0.0",
"lodash": "~4.0.0"
},
"homepage": "https://github.com/raintank/kentik-app-poc#readme"
}
......@@ -26,6 +26,10 @@
"small": "img/logo_small.png",
"large": "img/logo_large.png"
},
"screenshots": [
{"name": "img1", "path": "img/screenshot1.png"},
{"name": "img2", "path": "img/screenshot2.png"}
],
"links": [
{"name": "Project site", "url": "http://project.com"},
{"name": "License & Terms", "url": "http://license.com"}
......@@ -35,7 +39,8 @@
},
"includes": [
{"type": "dashboard", "name": "Nginx Connection stats", "path": "dashboards/nginx_connection_stats.json"},
{"type": "dashboard", "name": "Nginx Connections", "path": "dashboards/connections.json"},
{"type": "dashboard", "name": "Nginx Memory", "path": "dashboards/memory.json"},
{"type": "panel", "name": "Nginx Panel"},
{"type": "datasource", "name": "Nginx Datasource"}
],
......
{
"title": "Nginx Connections",
"revision": "1.5",
"schemaVersion": 11
}
require([
], function () {
function Dashboard() {
this.getInputs = function() {
};
this.buildDashboard = function() {
};
}
return Dashboard;
});
{
"title": "Nginx Memory",
"revision": "2.0",
"schemaVersion": 11
}
......@@ -102,8 +102,11 @@ func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
}
// GetString a
func (dash *Dashboard) GetString(prop string) string {
return dash.Data[prop].(string)
func (dash *Dashboard) GetString(prop string, defaultValue string) string {
if val, exists := dash.Data[prop]; exists {
return val.(string)
}
return defaultValue
}
// UpdateSlug updates the slug
......
package plugins
import (
"encoding/json"
"os"
"path/filepath"
"github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models"
)
type PluginDashboardInfoDTO struct {
Title string
InstalledURI string
InstalledRevision string
Revision string
Description string
}
func GetPluginDashboards(orgId int64, pluginId string) ([]*PluginDashboardInfoDTO, error) {
plugin, exists := Plugins[pluginId]
if !exists {
return nil, &PluginNotFoundError{pluginId}
}
result := make([]*PluginDashboardInfoDTO, 0)
for _, include := range plugin.Includes {
if include.Type == PluginTypeDashboard {
if dashInfo, err := getDashboardImportStatus(orgId, plugin, include); err != nil {
return nil, err
} else {
result = append(result, dashInfo)
}
}
}
return result, nil
}
func getDashboardImportStatus(orgId int64, plugin *PluginBase, dashInclude *PluginInclude) (*PluginDashboardInfoDTO, error) {
res := &PluginDashboardInfoDTO{}
dashboardFilePath := filepath.Join(plugin.PluginDir, dashInclude.Path)
reader, err := os.Open(dashboardFilePath)
if err != nil {
return nil, err
}
defer reader.Close()
jsonParser := json.NewDecoder(reader)
var data map[string]interface{}
if err := jsonParser.Decode(&data); err != nil {
return nil, err
}
dashboard := m.NewDashboardFromJson(data)
res.Title = dashboard.Title
res.Revision = dashboard.GetString("revision", "1.0")
query := m.GetDashboardQuery{OrgId: orgId, Slug: dashboard.Slug}
if err := bus.Dispatch(&query); err != nil {
if err != m.ErrDashboardNotFound {
return nil, err
}
} else {
res.InstalledURI = "db/" + query.Result.Slug
res.InstalledRevision = query.Result.GetString("revision", "1.0")
}
return res, nil
}
package plugins
import (
"testing"
"github.com/grafana/grafana/pkg/bus"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/ini.v1"
)
func TestPluginDashboards(t *testing.T) {
Convey("When asking plugin dashboard info", t, func() {
setting.Cfg = ini.Empty()
sec, _ := setting.Cfg.NewSection("plugin.nginx-app")
sec.NewKey("path", "../../examples/nginx-app")
err := Init()
So(err, ShouldBeNil)
bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
if query.Slug == "nginx-connections" {
dash := m.NewDashboard("Nginx Connections")
dash.Data["revision"] = "1.1"
query.Result = dash
return nil
}
return m.ErrDashboardNotFound
})
dashboards, err := GetPluginDashboards(1, "nginx-app")
So(err, ShouldBeNil)
Convey("should return 2 dashboarrd", func() {
So(len(dashboards), ShouldEqual, 2)
})
Convey("should include installed version info", func() {
So(dashboards[0].Title, ShouldEqual, "Nginx Connections")
So(dashboards[0].Revision, ShouldEqual, "1.5")
So(dashboards[0].InstalledRevision, ShouldEqual, "1.1")
So(dashboards[0].InstalledURI, ShouldEqual, "db/nginx-connections")
So(dashboards[1].Revision, ShouldEqual, "2.0")
So(dashboards[1].InstalledRevision, ShouldEqual, "")
})
})
}
......@@ -3,12 +3,28 @@ package plugins
import (
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/setting"
)
var (
PluginTypeApp = "app"
PluginTypeDatasource = "datasource"
PluginTypePanel = "panel"
PluginTypeDashboard = "dashboard"
)
type PluginNotFoundError struct {
PluginId string
}
func (e *PluginNotFoundError) Error() string {
return fmt.Sprintf("Plugin with id %s not found", e.PluginId)
}
type PluginLoader interface {
Load(decoder *json.Decoder, pluginDir string) error
}
......
......@@ -27,14 +27,15 @@ func TestPluginScans(t *testing.T) {
Convey("When reading app plugin definition", t, func() {
setting.Cfg = ini.Empty()
sec, _ := setting.Cfg.NewSection("plugin.app-test")
sec.NewKey("path", "../../tests/app-plugin-json")
sec, _ := setting.Cfg.NewSection("plugin.nginx-app")
sec.NewKey("path", "../../examples/nginx-app")
err := Init()
So(err, ShouldBeNil)
So(len(Apps), ShouldBeGreaterThan, 0)
So(Apps["app-example"].Info.Logos.Large, ShouldEqual, "public/plugins/app-example/img/logo_large.png")
So(Apps["app-example"].Info.Screenshots[1].Path, ShouldEqual, "public/plugins/app-example/img/screenshot2.png")
So(Apps["nginx-app"].Info.Logos.Large, ShouldEqual, "public/plugins/nginx-app/img/logo_large.png")
So(Apps["nginx-app"].Info.Screenshots[1].Path, ShouldEqual, "public/plugins/nginx-app/img/screenshot2.png")
})
}
///<reference path="../../../headers/common.d.ts" />
import angular from 'angular';
import _ from 'lodash';
import coreModule from 'app/core/core_module';
class DashboardScriptLoader {
}
export class DashImportListCtrl {
......@@ -12,9 +12,19 @@ export class DashImportListCtrl {
plugin: any;
constructor(private $http) {
this.dashboards = [];
this.plugin.includes
.filter(val => val.type === 'dashboard')
.forEach(this.getDashbordImportStatus.bind(this));
}
this.dashboards = this.plugin.includes.filter(val => val.type === 'dashboard');
getDashbordImportStatus(dash) {
var dashUrl = this.plugin.baseUrl + '/' + dash.path;
this.$http.get(dashUrl).then(res => {
this.load(res.data);
});
}
load(json) {
......@@ -22,10 +32,7 @@ export class DashImportListCtrl {
console.log(model);
}
import() {
// this.$http.get(url).then(res => {
// this.load(res.data);
// });
import(dash) {
}
}
......@@ -41,7 +48,7 @@ var template = `
{{dash.name}}</span>
</td>
<td class="width-2">
<button class="btn btn-primary btn-small">Install</button>
<button class="btn btn-secondary" ng-click="ctrl.import(dash)">Install</button>
</td
</tr>
</tbody>
......
......@@ -7,15 +7,14 @@
<h1 ng-show="isNew">Add data source</h1>
<h1 ng-show="!isNew">Edit data source</h1>
<div class="page-header-tabs">
<div class="page-header-tabs" ng-show="ctrl.hasDashboards">
<ul class="gf-tabs">
<li class="gf-tabs-item">
<a class="gf-tabs-link" ng-click="ctrl.tabIndex = 0" ng-class="{active: ctrl.tabIndex === 0}">
Config
</a>
</li>
<li class="gf-tabs-item" ng-show="ctrl.hasDashboards" ng-cloak>
<li class="gf-tabs-item">
<a class="gf-tabs-link" ng-click="ctrl.tabIndex = 1" ng-class="{active: ctrl.tabIndex === 1}">
Dashboards
</a>
......@@ -48,7 +47,6 @@
</div>
</div>
<rebuild-on-change property="ctrl.datasourceMeta.id">
<plugin-component type="datasource-config-ctrl">
</plugin-component>
......
{
"type": "app",
"name": "App Example",
"id": "app-example",
"staticRoot": ".",
"module": "app",
"pages": [
{"name": "Example1", "url": "/app-example", "reqRole": "Editor"}
],
"css": {
"light": "css/plugin.dark.css",
"dark": "css/plugin.light.css"
},
"info": {
"description": "Example Grafana App",
"author": {
"name": "Raintank Inc.",
"url": "http://raintank.io"
},
"keywords": ["example"],
"logos": {
"small": "img/logo_small.png",
"large": "img/logo_large.png"
},
"screenshots": [
{"name": "img1", "path": "img/screenshot1.png"},
{"name": "img2", "path": "img/screenshot2.png"}
],
"links": [
{"name": "Project site", "url": "http://project.com"},
{"name": "License & Terms", "url": "http://license.com"}
],
"version": "1.0.0",
"updated": "2015-02-10"
},
"dependencies": {
"grafanaVersion": "2.6.x",
"plugins": [
{"type": "datasource", "id": "graphite", "name": "Graphite", "version": "1.0.0"},
{"type": "panel", "id": "graph", "name": "Graph", "version": "1.0.0"}
]
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment