mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(plugins): worked on plugin dashboard import handling
This commit is contained in:
parent
78b9c3004a
commit
60adcedebe
@ -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"}
|
||||
],
|
||||
|
5
examples/nginx-app/src/dashboards/connections.json
Normal file
5
examples/nginx-app/src/dashboards/connections.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"title": "Nginx Connections",
|
||||
"revision": "1.5",
|
||||
"schemaVersion": 11
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
require([
|
||||
], function () {
|
||||
|
||||
function Dashboard() {
|
||||
|
||||
this.getInputs = function() {
|
||||
|
||||
};
|
||||
|
||||
this.buildDashboard = function() {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
return Dashboard;
|
||||
});
|
||||
|
5
examples/nginx-app/src/dashboards/memory.json
Normal file
5
examples/nginx-app/src/dashboards/memory.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"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
|
||||
|
77
pkg/plugins/dashboards.go
Normal file
77
pkg/plugins/dashboards.go
Normal file
@ -0,0 +1,77 @@
|
||||
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
|
||||
}
|
53
pkg/plugins/dashboards_test.go
Normal file
53
pkg/plugins/dashboards_test.go
Normal file
@ -0,0 +1,53 @@
|
||||
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")
|
||||
})
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
///<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.dashboards = this.plugin.includes.filter(val => val.type === 'dashboard');
|
||||
this.plugin.includes
|
||||
.filter(val => val.type === 'dashboard')
|
||||
.forEach(this.getDashbordImportStatus.bind(this));
|
||||
}
|
||||
|
||||
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>
|
||||
|
@ -1,48 +0,0 @@
|
||||
{
|
||||
"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"}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user