diff --git a/examples/nginx-app/package.json b/examples/nginx-app/package.json index 32c070f96b0..91c53734ec8 100644 --- a/examples/nginx-app/package.json +++ b/examples/nginx-app/package.json @@ -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" } diff --git a/examples/nginx-app/plugin.json b/examples/nginx-app/plugin.json index 65f01ad62fc..6de4fbd4daa 100644 --- a/examples/nginx-app/plugin.json +++ b/examples/nginx-app/plugin.json @@ -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"} ], diff --git a/examples/nginx-app/src/dashboards/connections.json b/examples/nginx-app/src/dashboards/connections.json new file mode 100644 index 00000000000..f1f62826f73 --- /dev/null +++ b/examples/nginx-app/src/dashboards/connections.json @@ -0,0 +1,5 @@ +{ + "title": "Nginx Connections", + "revision": "1.5", + "schemaVersion": 11 +} diff --git a/examples/nginx-app/src/dashboards/dashboard.js b/examples/nginx-app/src/dashboards/dashboard.js deleted file mode 100644 index 794e2c5217b..00000000000 --- a/examples/nginx-app/src/dashboards/dashboard.js +++ /dev/null @@ -1,17 +0,0 @@ -require([ -], function () { - - function Dashboard() { - - this.getInputs = function() { - - }; - - this.buildDashboard = function() { - - }; - } - - return Dashboard; -}); - diff --git a/examples/nginx-app/src/dashboards/memory.json b/examples/nginx-app/src/dashboards/memory.json new file mode 100644 index 00000000000..b79cb4d7dba --- /dev/null +++ b/examples/nginx-app/src/dashboards/memory.json @@ -0,0 +1,5 @@ +{ + "title": "Nginx Memory", + "revision": "2.0", + "schemaVersion": 11 +} diff --git a/examples/nginx-app/src/dashboards/nginx_connection_stats.json b/examples/nginx-app/src/dashboards/nginx_connection_stats.json deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index 1015dfbe28c..3e87f504a77 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -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 diff --git a/pkg/plugins/dashboards.go b/pkg/plugins/dashboards.go new file mode 100644 index 00000000000..f253db480a0 --- /dev/null +++ b/pkg/plugins/dashboards.go @@ -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 +} diff --git a/pkg/plugins/dashboards_test.go b/pkg/plugins/dashboards_test.go new file mode 100644 index 00000000000..b8ab51940a1 --- /dev/null +++ b/pkg/plugins/dashboards_test.go @@ -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, "") + }) + }) + +} diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index 0addaac9bb7..ac9fb5e04dc 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -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 } diff --git a/pkg/plugins/plugins_test.go b/pkg/plugins/plugins_test.go index 08ff3cbdfdd..b7f4b845fb5 100644 --- a/pkg/plugins/plugins_test.go +++ b/pkg/plugins/plugins_test.go @@ -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") }) } diff --git a/public/app/features/dashboard/import_list/import_list.ts b/public/app/features/dashboard/import_list/import_list.ts index b4fe3c36352..09a5b65de76 100644 --- a/public/app/features/dashboard/import_list/import_list.ts +++ b/public/app/features/dashboard/import_list/import_list.ts @@ -1,10 +1,10 @@ /// 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}} - + diff --git a/public/app/features/datasources/partials/edit.html b/public/app/features/datasources/partials/edit.html index fa1e0cee104..46177d998cc 100644 --- a/public/app/features/datasources/partials/edit.html +++ b/public/app/features/datasources/partials/edit.html @@ -7,15 +7,14 @@

Add data source

Edit data source

-
- +
- diff --git a/tests/app-plugin-json/plugin.json b/tests/app-plugin-json/plugin.json deleted file mode 100644 index 59f41766eda..00000000000 --- a/tests/app-plugin-json/plugin.json +++ /dev/null @@ -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"} - ] - } -}