From 3191678b14625e2edbe4207fd0ac2e037cb0ac44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 8 Mar 2016 20:46:25 +0100 Subject: [PATCH 01/20] feat(plugins): work on plugins that include dashboards --- .../dashboard/import_list/import_list.ts | 26 +++++++++++++------ public/app/features/datasources/edit_ctrl.js | 8 +++++- .../features/datasources/partials/edit.html | 4 +-- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/public/app/features/dashboard/import_list/import_list.ts b/public/app/features/dashboard/import_list/import_list.ts index 1d3aa42611b..d8333bec4fa 100644 --- a/public/app/features/dashboard/import_list/import_list.ts +++ b/public/app/features/dashboard/import_list/import_list.ts @@ -8,8 +8,13 @@ class DashboardScriptLoader { } export class DashImportListCtrl { + dashboards: any[]; + plugin: any; + constructor(private $http) { - console.log('importList', this); + + this.dashboards = this.plugin.includes.filter(val => val.type === 'dashboard'); + } load(json) { @@ -18,17 +23,22 @@ export class DashImportListCtrl { } import() { - var url = 'public/app/plugins/datasource/graphite/dashboards/carbon_stats.json'; - this.$http.get(url).then(res => { - this.load(res.data); - }); + // this.$http.get(url).then(res => { + // this.load(res.data); + // }); } } var template = ` -

Dashboards

-
- +
+

Dashboards

+
+ + + +
`; diff --git a/public/app/features/datasources/edit_ctrl.js b/public/app/features/datasources/edit_ctrl.js index 062cec6e5e9..4e99dc211ff 100644 --- a/public/app/features/datasources/edit_ctrl.js +++ b/public/app/features/datasources/edit_ctrl.js @@ -18,7 +18,13 @@ function (angular, _, config) { module.controller('DataSourceEditCtrl', function($scope, $q, backendSrv, $routeParams, $location, datasourceSrv) { - var defaults = {name: '', type: 'graphite', url: '', access: 'proxy', jsonData: {}}; + var defaults = { + name: '', + type: 'graphite', + url: '', + access: 'proxy', + jsonData: {} + }; $scope.init = function() { $scope.isNew = true; diff --git a/public/app/features/datasources/partials/edit.html b/public/app/features/datasources/partials/edit.html index 39639206c23..9176ff4f4ac 100644 --- a/public/app/features/datasources/partials/edit.html +++ b/public/app/features/datasources/partials/edit.html @@ -36,6 +36,8 @@ icon="icon-gf icon-gf-datasources"> + +
@@ -47,8 +49,6 @@ icon="icon-gf icon-gf-datasources">
- -
From 1951f3856f2427b4d3462d35881b80f943c5f6f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 10 Mar 2016 09:47:29 +0100 Subject: [PATCH 02/20] feat(plugins): refactored datasourceEditCtrl to typescript, #4298 --- .../app/core/directives/plugin_component.ts | 9 +- public/app/core/routes/routes.ts | 1 + public/app/features/datasources/edit_ctrl.js | 129 ---------------- public/app/features/datasources/edit_ctrl.ts | 140 ++++++++++++++++++ .../features/datasources/partials/edit.html | 41 +++-- 5 files changed, 174 insertions(+), 146 deletions(-) delete mode 100644 public/app/features/datasources/edit_ctrl.js create mode 100644 public/app/features/datasources/edit_ctrl.ts diff --git a/public/app/core/directives/plugin_component.ts b/public/app/core/directives/plugin_component.ts index 858aab02035..69535327cc8 100644 --- a/public/app/core/directives/plugin_component.ts +++ b/public/app/core/directives/plugin_component.ts @@ -148,12 +148,13 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $ } // ConfigCtrl case 'datasource-config-ctrl': { - return System.import(scope.datasourceMeta.module).then(function(dsModule) { + var dsMeta = scope.ctrl.datasourceMeta; + return System.import(dsMeta.module).then(function(dsModule) { return { - baseUrl: scope.datasourceMeta.baseUrl, - name: 'ds-config-' + scope.datasourceMeta.id, + baseUrl: dsMeta.baseUrl, + name: 'ds-config-' + dsMeta.id, bindings: {meta: "=", current: "="}, - attrs: {meta: "datasourceMeta", current: "current"}, + attrs: {meta: "ctrl.datasourceMeta", current: "ctrl.current"}, Component: dsModule.ConfigCtrl, }; }); diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts index 6e3af683c82..2b035650c76 100644 --- a/public/app/core/routes/routes.ts +++ b/public/app/core/routes/routes.ts @@ -57,6 +57,7 @@ function setupAngularRoutes($routeProvider, $locationProvider) { .when('/datasources/edit/:id', { templateUrl: 'public/app/features/datasources/partials/edit.html', controller : 'DataSourceEditCtrl', + controllerAs: 'ctrl', resolve: loadOrgBundle, }) .when('/datasources/new', { diff --git a/public/app/features/datasources/edit_ctrl.js b/public/app/features/datasources/edit_ctrl.js deleted file mode 100644 index 4e99dc211ff..00000000000 --- a/public/app/features/datasources/edit_ctrl.js +++ /dev/null @@ -1,129 +0,0 @@ -define([ - 'angular', - 'lodash', - 'app/core/config', -], -function (angular, _, config) { - 'use strict'; - - var module = angular.module('grafana.controllers'); - var datasourceTypes = []; - - module.directive('datasourceHttpSettings', function() { - return { - scope: {current: "="}, - templateUrl: 'public/app/features/datasources/partials/http_settings.html' - }; - }); - - module.controller('DataSourceEditCtrl', function($scope, $q, backendSrv, $routeParams, $location, datasourceSrv) { - - var defaults = { - name: '', - type: 'graphite', - url: '', - access: 'proxy', - jsonData: {} - }; - - $scope.init = function() { - $scope.isNew = true; - $scope.datasources = []; - - $scope.loadDatasourceTypes().then(function() { - if ($routeParams.id) { - $scope.getDatasourceById($routeParams.id); - } else { - $scope.current = angular.copy(defaults); - $scope.typeChanged(); - } - }); - }; - - $scope.loadDatasourceTypes = function() { - if (datasourceTypes.length > 0) { - $scope.types = datasourceTypes; - return $q.when(null); - } - - return backendSrv.get('/api/org/plugins', {enabled: 1, type: 'datasource'}).then(function(plugins) { - datasourceTypes = plugins; - $scope.types = plugins; - }); - }; - - $scope.getDatasourceById = function(id) { - backendSrv.get('/api/datasources/' + id).then(function(ds) { - $scope.isNew = false; - $scope.current = ds; - return $scope.typeChanged(); - }); - }; - - $scope.typeChanged = function() { - return backendSrv.get('/api/org/plugins/' + $scope.current.type + '/settings').then(function(pluginInfo) { - $scope.datasourceMeta = pluginInfo; - }); - }; - - $scope.updateFrontendSettings = function() { - return backendSrv.get('/api/frontend/settings').then(function(settings) { - config.datasources = settings.datasources; - config.defaultDatasource = settings.defaultDatasource; - datasourceSrv.init(); - }); - }; - - $scope.testDatasource = function() { - $scope.testing = { done: false }; - - datasourceSrv.get($scope.current.name).then(function(datasource) { - if (!datasource.testDatasource) { - $scope.testing.message = 'Data source does not support test connection feature.'; - $scope.testing.status = 'warning'; - $scope.testing.title = 'Unknown'; - return; - } - - return datasource.testDatasource().then(function(result) { - $scope.testing.message = result.message; - $scope.testing.status = result.status; - $scope.testing.title = result.title; - }, function(err) { - if (err.statusText) { - $scope.testing.message = err.statusText; - $scope.testing.title = "HTTP Error"; - } else { - $scope.testing.message = err.message; - $scope.testing.title = "Unknown error"; - } - }); - }).finally(function() { - $scope.testing.done = true; - }); - }; - - $scope.saveChanges = function(test) { - if (!$scope.editForm.$valid) { - return; - } - - if ($scope.current.id) { - return backendSrv.put('/api/datasources/' + $scope.current.id, $scope.current).then(function() { - $scope.updateFrontendSettings().then(function() { - if (test) { - $scope.testDatasource(); - } - }); - }); - } else { - return backendSrv.post('/api/datasources', $scope.current).then(function(result) { - $scope.updateFrontendSettings(); - $location.path('datasources/edit/' + result.id); - }); - } - }; - - $scope.init(); - }); -}); diff --git a/public/app/features/datasources/edit_ctrl.ts b/public/app/features/datasources/edit_ctrl.ts new file mode 100644 index 00000000000..eafee0d427a --- /dev/null +++ b/public/app/features/datasources/edit_ctrl.ts @@ -0,0 +1,140 @@ +/// + +import angular from 'angular'; +import _ from 'lodash'; +import coreModule from 'app/core/core_module'; +import config from 'app/core/config'; + +var datasourceTypes = []; + +var defaults = { + name: '', + type: 'graphite', + url: '', + access: 'proxy', + jsonData: {} +}; + +export class DataSourceEditCtrl { + isNew: boolean; + datasources: any[]; + current: any; + types: any; + testing: any; + datasourceMeta: any; + + /** @ngInject */ + constructor( + private $scope, + private $q, + private backendSrv, + private $routeParams, + private $location, + private datasourceSrv) { + + this.isNew = true; + this.datasources = []; + + this.loadDatasourceTypes().then(() => { + if (this.$routeParams.id) { + this.getDatasourceById(this.$routeParams.id); + } else { + this.current = angular.copy(defaults); + this.typeChanged(); + } + }); + } + + loadDatasourceTypes() { + if (datasourceTypes.length > 0) { + this.types = datasourceTypes; + return this.$q.when(null); + } + + return this.backendSrv.get('/api/org/plugins', {enabled: 1, type: 'datasource'}).then(plugins => { + datasourceTypes = plugins; + this.types = plugins; + }); + } + + getDatasourceById(id) { + this.backendSrv.get('/api/datasources/' + id).then(ds => { + this.isNew = false; + this.current = ds; + return this.typeChanged(); + }); + } + + typeChanged() { + return this.backendSrv.get('/api/org/plugins/' + this.current.type + '/settings').then(pluginInfo => { + this.datasourceMeta = pluginInfo; + }); + } + + updateFrontendSettings() { + return this.backendSrv.get('/api/frontend/settings').then(settings => { + config.datasources = settings.datasources; + config.defaultDatasource = settings.defaultDatasource; + this.datasourceSrv.init(); + }); + } + + testDatasource() { + this.testing = { done: false }; + + this.datasourceSrv.get(this.current.name).then(datasource => { + if (!datasource.testDatasource) { + this.testing.message = 'Data source does not support test connection feature.'; + this.testing.status = 'warning'; + this.testing.title = 'Unknown'; + return; + } + + return datasource.testDatasource().then(result => { + this.testing.message = result.message; + this.testing.status = result.status; + this.testing.title = result.title; + }).catch(err => { + if (err.statusText) { + this.testing.message = err.statusText; + this.testing.title = "HTTP Error"; + } else { + this.testing.message = err.message; + this.testing.title = "Unknown error"; + } + }); + }).finally(() => { + this.testing.done = true; + }); + } + + saveChanges(test) { + if (!this.$scope.editForm.$valid) { + return; + } + + if (this.current.id) { + return this.backendSrv.put('/api/datasources/' + this.current.id, this.current).then(() => { + this.updateFrontendSettings().then(() => { + if (test) { + this.testDatasource(); + } + }); + }); + } else { + return this.backendSrv.post('/api/datasources', this.current).then(result => { + this.updateFrontendSettings(); + this.$location.path('datasources/edit/' + result.id); + }); + } + }; +} + +coreModule.controller('DataSourceEditCtrl', DataSourceEditCtrl); + +coreModule.directive('datasourceHttpSettings', function() { + return { + scope: {current: "="}, + templateUrl: 'public/app/features/datasources/partials/http_settings.html' + }; +}); diff --git a/public/app/features/datasources/partials/edit.html b/public/app/features/datasources/partials/edit.html index 9176ff4f4ac..c8840321f40 100644 --- a/public/app/features/datasources/partials/edit.html +++ b/public/app/features/datasources/partials/edit.html @@ -1,43 +1,58 @@ - +
- +
- -
-
- Name - - - The name is used when you select the data source in panels. - The Default data source is preselected in new - panels. - + +
+
+ Name + + + The name is used when you select the data source in panels. + The Default data source is preselected in new + panels. + - -
+ +
-
- Type -
- -
-
-
+
+ Type +
+ +
+
+
- - - + + + + - - +
+
Testing....
+
Test results
+
+
{{testing.title}}
+
+
+
-
-
Testing....
-
Test results
-
-
{{testing.title}}
-
-
-
+
+ + + + Cancel +
-
- - - - Cancel -
+ +
+ +
+ +
-
diff --git a/public/app/plugins/datasource/graphite/plugin.json b/public/app/plugins/datasource/graphite/plugin.json index b079ec320f2..c923cb79c4e 100644 --- a/public/app/plugins/datasource/graphite/plugin.json +++ b/public/app/plugins/datasource/graphite/plugin.json @@ -4,7 +4,8 @@ "id": "graphite", "includes": [ - {"type": "dashboard", "name": "Carbon Stats", "path": "dashboards/carbon_stats.json"} + {"type": "dashboard", "name": "Carbon Overview", "path": "dashboards/carbon_stats.json"}, + {"type": "dashboard", "name": "Carbon Agent Details", "path": "dashboards/carbon_stats.json"} ], "metrics": true, From 60adcedebe6c9a48535bac83f767179f420dc1ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 10 Mar 2016 19:57:48 +0100 Subject: [PATCH 04/20] feat(plugins): worked on plugin dashboard import handling --- examples/nginx-app/package.json | 2 +- examples/nginx-app/plugin.json | 7 +- .../nginx-app/src/dashboards/connections.json | 5 ++ .../nginx-app/src/dashboards/dashboard.js | 17 ---- examples/nginx-app/src/dashboards/memory.json | 5 ++ .../dashboards/nginx_connection_stats.json | 0 pkg/models/dashboards.go | 7 +- pkg/plugins/dashboards.go | 77 +++++++++++++++++++ pkg/plugins/dashboards_test.go | 53 +++++++++++++ pkg/plugins/models.go | 16 ++++ pkg/plugins/plugins_test.go | 9 ++- .../dashboard/import_list/import_list.ts | 21 +++-- .../features/datasources/partials/edit.html | 6 +- tests/app-plugin-json/plugin.json | 48 ------------ 14 files changed, 189 insertions(+), 84 deletions(-) create mode 100644 examples/nginx-app/src/dashboards/connections.json delete mode 100644 examples/nginx-app/src/dashboards/dashboard.js create mode 100644 examples/nginx-app/src/dashboards/memory.json delete mode 100644 examples/nginx-app/src/dashboards/nginx_connection_stats.json create mode 100644 pkg/plugins/dashboards.go create mode 100644 pkg/plugins/dashboards_test.go delete mode 100644 tests/app-plugin-json/plugin.json 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"} - ] - } -} From 8c6890c95fa4a824d5ace0cddc7783b8b2a4f688 Mon Sep 17 00:00:00 2001 From: Tom Dyas Date: Thu, 10 Mar 2016 17:46:59 -0500 Subject: [PATCH 05/20] remove extra comma in nginx-app's package.json otherwise grunt fails due to a syntax error in package.json --- examples/nginx-app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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" } From 2c5abed2f1bc3a9a2c9c81c58fe9c4ed3a810eff Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Fri, 11 Mar 2016 12:11:01 +0900 Subject: [PATCH 06/20] (cloudwatch) add metrics/dimensions of AWS/Events and AWS/Logs --- pkg/api/cloudwatch/metrics.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/api/cloudwatch/metrics.go b/pkg/api/cloudwatch/metrics.go index a4e97cdb347..54ab565bede 100644 --- a/pkg/api/cloudwatch/metrics.go +++ b/pkg/api/cloudwatch/metrics.go @@ -55,8 +55,10 @@ func init() { "S3BytesWritten", "S3BytesRead", "HDFSUtilization", "HDFSBytesRead", "HDFSBytesWritten", "MissingBlocks", "CorruptBlocks", "TotalLoad", "MemoryTotalMB", "MemoryReservedMB", "MemoryAvailableMB", "MemoryAllocatedMB", "PendingDeletionBlocks", "UnderReplicatedBlocks", "DfsPendingReplicationBlocks", "CapacityRemainingGB", "HbaseBackupFailed", "MostRecentBackupDuration", "TimeSinceLastSuccessfulBackup"}, "AWS/ES": {"ClusterStatus.green", "ClusterStatus.yellow", "ClusterStatus.red", "Nodes", "SearchableDocuments", "DeletedDocuments", "CPUUtilization", "FreeStorageSpace", "JVMMemoryPressure", "AutomatedSnapshotFailure", "MasterCPUUtilization", "MasterFreeStorageSpace", "MasterJVMMemoryPressure", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "DiskQueueLength", "ReadIOPS", "WriteIOPS"}, + "AWS/Events": {"Invocations", "FailedInvocations", "TriggeredRules", "MatchedEvents", "ThrottledRules"}, "AWS/Kinesis": {"PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "IncomingBytes", "IncomingRecords", "GetRecords.Bytes", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Success"}, "AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles"}, + "AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"}, "AWS/ML": {"PredictCount", "PredictFailureCount"}, "AWS/OpsWorks": {"cpu_idle", "cpu_nice", "cpu_system", "cpu_user", "cpu_waitio", "load_1", "load_5", "load_15", "memory_buffers", "memory_cached", "memory_free", "memory_swap", "memory_total", "memory_used", "procs"}, "AWS/Redshift": {"CPUUtilization", "DatabaseConnections", "HealthStatus", "MaintenanceMode", "NetworkReceiveThroughput", "NetworkTransmitThroughput", "PercentageDiskSpaceUsed", "ReadIOPS", "ReadLatency", "ReadThroughput", "WriteIOPS", "WriteLatency", "WriteThroughput"}, @@ -85,8 +87,10 @@ func init() { "AWS/ELB": {"LoadBalancerName", "AvailabilityZone"}, "AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"}, "AWS/ES": {}, + "AWS/Events": {"RuleName"}, "AWS/Kinesis": {"StreamName"}, "AWS/Lambda": {"FunctionName"}, + "AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"}, "AWS/ML": {"MLModelId", "RequestMode"}, "AWS/OpsWorks": {"StackId", "LayerId", "InstanceId"}, "AWS/Redshift": {"NodeID", "ClusterIdentifier"}, From 336c1b7b6e17292d033181cfa0d51e633ed705b1 Mon Sep 17 00:00:00 2001 From: simnv Date: Fri, 11 Mar 2016 08:30:56 +0500 Subject: [PATCH 07/20] Fix groupby selection Default groupby selection for influxdb is escaped not like a regex, but as a glob. It works fine with one value in variable, but when you select multiple values, query is incorrect: > SHOW TAG KEYS FROM "os.disk.fs.space_total" WHERE "host" =~ /{host-1,host-2}$/ --- public/app/plugins/datasource/influxdb/datasource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/influxdb/datasource.ts b/public/app/plugins/datasource/influxdb/datasource.ts index 84c7a83394c..1136c9709bd 100644 --- a/public/app/plugins/datasource/influxdb/datasource.ts +++ b/public/app/plugins/datasource/influxdb/datasource.ts @@ -104,7 +104,7 @@ export function InfluxDatasource(instanceSettings, $q, backendSrv, templateSrv) this.metricFindQuery = function (query) { var interpolated; try { - interpolated = templateSrv.replace(query); + interpolated = templateSrv.replace(query, null, 'regex'); } catch (err) { return $q.reject(err); } From b72cf90ed43b29f36f79a38fd5be5bc7da6fc32f Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 11 Mar 2016 08:39:05 +0100 Subject: [PATCH 08/20] example(panel): rename to match new convetion --- .../css/styles.css | 0 .../{panel-boilerplate-es5 => boilerplate-es5-panel}/module.js | 0 .../{panel-boilerplate-es5 => boilerplate-es5-panel}/panel.html | 0 .../{panel-boilerplate-es5 => boilerplate-es5-panel}/plugin.json | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename examples/{panel-boilerplate-es5 => boilerplate-es5-panel}/css/styles.css (100%) rename examples/{panel-boilerplate-es5 => boilerplate-es5-panel}/module.js (100%) rename examples/{panel-boilerplate-es5 => boilerplate-es5-panel}/panel.html (100%) rename examples/{panel-boilerplate-es5 => boilerplate-es5-panel}/plugin.json (100%) diff --git a/examples/panel-boilerplate-es5/css/styles.css b/examples/boilerplate-es5-panel/css/styles.css similarity index 100% rename from examples/panel-boilerplate-es5/css/styles.css rename to examples/boilerplate-es5-panel/css/styles.css diff --git a/examples/panel-boilerplate-es5/module.js b/examples/boilerplate-es5-panel/module.js similarity index 100% rename from examples/panel-boilerplate-es5/module.js rename to examples/boilerplate-es5-panel/module.js diff --git a/examples/panel-boilerplate-es5/panel.html b/examples/boilerplate-es5-panel/panel.html similarity index 100% rename from examples/panel-boilerplate-es5/panel.html rename to examples/boilerplate-es5-panel/panel.html diff --git a/examples/panel-boilerplate-es5/plugin.json b/examples/boilerplate-es5-panel/plugin.json similarity index 100% rename from examples/panel-boilerplate-es5/plugin.json rename to examples/boilerplate-es5-panel/plugin.json From 0052e9d13623961b4abe3a7c18fa88030b9427d8 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Fri, 11 Mar 2016 14:58:29 +0900 Subject: [PATCH 09/20] (cloudwatch) add ec2_instance_attribute() query --- .../datasource/cloudwatch/datasource.js | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index 06c723e416a..5d093640b1e 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -143,7 +143,7 @@ function (angular, _, moment, dateMath, CloudWatchAnnotationQuery) { return this.awsRequest({ region: region, action: 'DescribeInstances', - parameters: { filter: filters, instanceIds: instanceIds } + parameters: { filters: filters, instanceIds: instanceIds } }); }; @@ -205,6 +205,28 @@ function (angular, _, moment, dateMath, CloudWatchAnnotationQuery) { }); } + var ec2InstanceAttributeQuery = query.match(/^ec2_instance_attribute\(([^,]+?),\s?([^,]+?),\s?([^)]+)\)/); + if (ec2InstanceAttributeQuery) { + region = templateSrv.replace(ec2InstanceAttributeQuery[1]); + var filterJson = JSON.parse(templateSrv.replace(ec2InstanceAttributeQuery[3])); + var filter = _.map(filterJson, function(f) { + return { + Name: f.slice(0, f.indexOf('=')), + Values: f.slice(f.indexOf('=') + 1).split(',') + }; + }); + var targetAttributeName = templateSrv.replace(ec2InstanceAttributeQuery[2]); + + return this.performEC2DescribeInstances(region, filter, null).then(function(result) { + var attributes = _.chain(result.Reservations) + .map(function(reservations) { + return _.pluck(reservations.Instances, targetAttributeName); + }) + .flatten().value(); + return transformSuggestData(attributes); + }); + } + return $q.when([]); }; From a16c799da931319f24178ed06f9932b999575544 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 11 Mar 2016 08:52:14 +0100 Subject: [PATCH 10/20] fix(examples): update link in readme --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 2e8a46f2aa3..04a544be399 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,3 +1,3 @@ ## Example plugin implementations -[datasource-plugin-genericdatsource](https://github.com/grafana/datasource-plugin-genericdatasource/tree/3.0) \ No newline at end of file +[simple-json-datasource](https://github.com/grafana/simple-json-datasource) \ No newline at end of file From 25f08ddd39661b571d767a8ecae07f13cd6eca1d Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Fri, 11 Mar 2016 16:59:06 +0900 Subject: [PATCH 11/20] (cloudwatch) change parameter form of ec2_instance_attribute() --- public/app/plugins/datasource/cloudwatch/datasource.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index 5d093640b1e..cdaeebb83b8 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -205,14 +205,14 @@ function (angular, _, moment, dateMath, CloudWatchAnnotationQuery) { }); } - var ec2InstanceAttributeQuery = query.match(/^ec2_instance_attribute\(([^,]+?),\s?([^,]+?),\s?([^)]+)\)/); + var ec2InstanceAttributeQuery = query.match(/^ec2_instance_attribute\(([^,]+?),\s?([^,]+?),\s?(.+?)\)/); if (ec2InstanceAttributeQuery) { region = templateSrv.replace(ec2InstanceAttributeQuery[1]); var filterJson = JSON.parse(templateSrv.replace(ec2InstanceAttributeQuery[3])); - var filter = _.map(filterJson, function(f) { + var filter = _.map(filterJson, function(values, name) { return { - Name: f.slice(0, f.indexOf('=')), - Values: f.slice(f.indexOf('=') + 1).split(',') + Name: name, + Values: values }; }); var targetAttributeName = templateSrv.replace(ec2InstanceAttributeQuery[2]); From b989e2ce37fdaa9b14f142e6c61b309123f6aa0a Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Fri, 11 Mar 2016 17:04:42 +0900 Subject: [PATCH 12/20] minor fix --- public/app/plugins/datasource/cloudwatch/datasource.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index cdaeebb83b8..704cbd41e72 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -209,7 +209,7 @@ function (angular, _, moment, dateMath, CloudWatchAnnotationQuery) { if (ec2InstanceAttributeQuery) { region = templateSrv.replace(ec2InstanceAttributeQuery[1]); var filterJson = JSON.parse(templateSrv.replace(ec2InstanceAttributeQuery[3])); - var filter = _.map(filterJson, function(values, name) { + var filters = _.map(filterJson, function(values, name) { return { Name: name, Values: values @@ -217,7 +217,7 @@ function (angular, _, moment, dateMath, CloudWatchAnnotationQuery) { }); var targetAttributeName = templateSrv.replace(ec2InstanceAttributeQuery[2]); - return this.performEC2DescribeInstances(region, filter, null).then(function(result) { + return this.performEC2DescribeInstances(region, filters, null).then(function(result) { var attributes = _.chain(result.Reservations) .map(function(reservations) { return _.pluck(reservations.Instances, targetAttributeName); From 2de439bd1ebf6adbfcd4c962fe57ee6c16bf8c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 11 Mar 2016 09:57:20 +0100 Subject: [PATCH 13/20] feat(plugins): progress on dashboard installs , #4298 --- pkg/api/api.go | 14 +++-- pkg/api/dtos/plugins.go | 6 ++ pkg/api/{plugin_setting.go => plugins.go} | 31 ++++++++++ .../{plugin_setting.go => plugin_settings.go} | 0 pkg/plugins/dashboard_installer.go | 56 +++++++++++++++++++ pkg/plugins/dashboards.go | 36 ++++++++---- pkg/plugins/models.go | 2 +- .../dashboard/import_list/import_list.ts | 39 +++++++------ public/app/features/datasources/edit_ctrl.ts | 4 +- public/app/features/plugins/edit_ctrl.ts | 2 +- public/app/features/plugins/list_ctrl.ts | 2 +- 11 files changed, 154 insertions(+), 38 deletions(-) rename pkg/api/{plugin_setting.go => plugins.go} (77%) rename pkg/models/{plugin_setting.go => plugin_settings.go} (100%) create mode 100644 pkg/plugins/dashboard_installer.go diff --git a/pkg/api/api.go b/pkg/api/api.go index 0915bbd678d..44b56ea62f6 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -126,10 +126,6 @@ func Register(r *macaron.Macaron) { r.Post("/invites", quota("user"), bind(dtos.AddInviteForm{}), wrap(AddOrgInvite)) r.Patch("/invites/:code/revoke", wrap(RevokeInvite)) - // apps - r.Get("/plugins", wrap(GetPluginList)) - r.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingById)) - r.Post("/plugins/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting)) }, reqOrgAdmin) // create new org @@ -177,6 +173,16 @@ func Register(r *macaron.Macaron) { r.Get("/", wrap(GetDataSourceByName)) }, reqOrgAdmin) + r.Group("/plugins", func() { + r.Get("/", wrap(GetPluginList)) + + r.Get("/dashboards/:pluginId", wrap(GetPluginDashboards)) + r.Post("/dashboards/install", bind(dtos.InstallPluginDashboardCmd{}), wrap(InstallPluginDashboard)) + + r.Get("/:pluginId/settings", wrap(GetPluginSettingById)) + r.Post("/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting)) + }, reqOrgAdmin) + r.Get("/frontend/settings/", GetFrontendSettings) r.Any("/datasources/proxy/:id/*", reqSignedIn, ProxyDataSourceRequest) r.Any("/datasources/proxy/:id", reqSignedIn, ProxyDataSourceRequest) diff --git a/pkg/api/dtos/plugins.go b/pkg/api/dtos/plugins.go index 61b149d73e4..144b9b09755 100644 --- a/pkg/api/dtos/plugins.go +++ b/pkg/api/dtos/plugins.go @@ -25,3 +25,9 @@ type PluginListItem struct { Pinned bool `json:"pinned"` Info *plugins.PluginInfo `json:"info"` } + +type InstallPluginDashboardCmd struct { + PluginId string `json:"pluginId"` + Path string `json:"path"` + Inputs map[string]interface{} `json:"inputs"` +} diff --git a/pkg/api/plugin_setting.go b/pkg/api/plugins.go similarity index 77% rename from pkg/api/plugin_setting.go rename to pkg/api/plugins.go index 1ceecdfac18..1ba0dde6c9f 100644 --- a/pkg/api/plugin_setting.go +++ b/pkg/api/plugins.go @@ -107,3 +107,34 @@ func UpdatePluginSetting(c *middleware.Context, cmd m.UpdatePluginSettingCmd) Re return ApiSuccess("Plugin settings updated") } + +func GetPluginDashboards(c *middleware.Context) Response { + pluginId := c.Params(":pluginId") + + if list, err := plugins.GetPluginDashboards(c.OrgId, pluginId); err != nil { + if notfound, ok := err.(plugins.PluginNotFoundError); ok { + return ApiError(404, notfound.Error(), nil) + } + + return ApiError(500, "Failed to get plugin dashboards", err) + } else { + return Json(200, list) + } +} + +func InstallPluginDashboard(c *middleware.Context, apiCmd dtos.InstallPluginDashboardCmd) Response { + + cmd := plugins.InstallPluginDashboardCommand{ + OrgId: c.OrgId, + UserId: c.UserId, + PluginId: apiCmd.PluginId, + Path: apiCmd.Path, + Inputs: apiCmd.Inputs, + } + + if err := bus.Dispatch(&cmd); err != nil { + return ApiError(500, "Failed to install dashboard", err) + } + + return Json(200, cmd.Result) +} diff --git a/pkg/models/plugin_setting.go b/pkg/models/plugin_settings.go similarity index 100% rename from pkg/models/plugin_setting.go rename to pkg/models/plugin_settings.go diff --git a/pkg/plugins/dashboard_installer.go b/pkg/plugins/dashboard_installer.go new file mode 100644 index 00000000000..9f52b518d70 --- /dev/null +++ b/pkg/plugins/dashboard_installer.go @@ -0,0 +1,56 @@ +package plugins + +import ( + "github.com/grafana/grafana/pkg/bus" + m "github.com/grafana/grafana/pkg/models" +) + +type InstallPluginDashboardCommand struct { + Path string `json:"string"` + Inputs map[string]interface{} `json:"inputs"` + + OrgId int64 `json:"-"` + UserId int64 `json:"-"` + PluginId string `json:"-"` + Result *PluginDashboardInfoDTO +} + +func init() { + bus.AddHandler("plugins", InstallPluginDashboard) +} + +func InstallPluginDashboard(cmd *InstallPluginDashboardCommand) error { + plugin, exists := Plugins[cmd.PluginId] + + if !exists { + return PluginNotFoundError{cmd.PluginId} + } + + var dashboard *m.Dashboard + var err error + + if dashboard, err = loadPluginDashboard(plugin, cmd.Path); err != nil { + return err + } + + saveCmd := m.SaveDashboardCommand{ + Dashboard: dashboard.Data, + OrgId: cmd.OrgId, + UserId: cmd.UserId, + } + + if err := bus.Dispatch(&saveCmd); err != nil { + return err + } + + cmd.Result = &PluginDashboardInfoDTO{ + PluginId: cmd.PluginId, + Title: dashboard.Title, + Path: cmd.Path, + Revision: dashboard.GetString("revision", "1.0"), + InstalledURI: "db/" + saveCmd.Result.Slug, + InstalledRevision: dashboard.GetString("revision", "1.0"), + } + + return nil +} diff --git a/pkg/plugins/dashboards.go b/pkg/plugins/dashboards.go index f253db480a0..9acb64cec1b 100644 --- a/pkg/plugins/dashboards.go +++ b/pkg/plugins/dashboards.go @@ -10,25 +10,27 @@ import ( ) type PluginDashboardInfoDTO struct { - Title string - InstalledURI string - InstalledRevision string - Revision string - Description string + PluginId string `json:"pluginId"` + Title string `json:"title"` + InstalledURI string `json:"installedURI"` + InstalledRevision string `json:"installedRevision"` + Revision string `json:"revision"` + Description string `json:"description"` + Path string `json:"path"` } func GetPluginDashboards(orgId int64, pluginId string) ([]*PluginDashboardInfoDTO, error) { plugin, exists := Plugins[pluginId] if !exists { - return nil, &PluginNotFoundError{pluginId} + 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 { + if dashInfo, err := getDashboardImportStatus(orgId, plugin, include.Path); err != nil { return nil, err } else { result = append(result, dashInfo) @@ -39,10 +41,9 @@ func GetPluginDashboards(orgId int64, pluginId string) ([]*PluginDashboardInfoDT return result, nil } -func getDashboardImportStatus(orgId int64, plugin *PluginBase, dashInclude *PluginInclude) (*PluginDashboardInfoDTO, error) { - res := &PluginDashboardInfoDTO{} +func loadPluginDashboard(plugin *PluginBase, path string) (*m.Dashboard, error) { - dashboardFilePath := filepath.Join(plugin.PluginDir, dashInclude.Path) + dashboardFilePath := filepath.Join(plugin.PluginDir, path) reader, err := os.Open(dashboardFilePath) if err != nil { return nil, err @@ -57,8 +58,21 @@ func getDashboardImportStatus(orgId int64, plugin *PluginBase, dashInclude *Plug return nil, err } - dashboard := m.NewDashboardFromJson(data) + return m.NewDashboardFromJson(data), nil +} +func getDashboardImportStatus(orgId int64, plugin *PluginBase, path string) (*PluginDashboardInfoDTO, error) { + res := &PluginDashboardInfoDTO{} + + var dashboard *m.Dashboard + var err error + + if dashboard, err = loadPluginDashboard(plugin, path); err != nil { + return nil, err + } + + res.Path = path + res.PluginId = plugin.Id res.Title = dashboard.Title res.Revision = dashboard.GetString("revision", "1.0") diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index ac9fb5e04dc..502c355eda8 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -21,7 +21,7 @@ type PluginNotFoundError struct { PluginId string } -func (e *PluginNotFoundError) Error() string { +func (e PluginNotFoundError) Error() string { return fmt.Sprintf("Plugin with id %s not found", e.PluginId) } diff --git a/public/app/features/dashboard/import_list/import_list.ts b/public/app/features/dashboard/import_list/import_list.ts index 09a5b65de76..7d88d7f9f0a 100644 --- a/public/app/features/dashboard/import_list/import_list.ts +++ b/public/app/features/dashboard/import_list/import_list.ts @@ -11,29 +11,26 @@ export class DashImportListCtrl { dashboards: any[]; plugin: any; - constructor(private $http) { + constructor(private $http, private backendSrv, private $rootScope) { this.dashboards = []; - 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); - + backendSrv.get(`/api/plugins/dashboards/${this.plugin.id}`).then(dashboards => { + this.dashboards = dashboards; }); } - load(json) { - var model = angular.fromJson(json); - console.log(model); + import(dash) { + var installCmd = { + pluginId: this.plugin.id, + path: dash.path, + inputs: {} + }; + + this.backendSrv.post(`/api/plugins/dashboards/install`, installCmd).then(res => { + console.log(res); + }); } - import(dash) { - } } var template = ` @@ -45,11 +42,17 @@ var template = ` - {{dash.name}} + {{dash.title}} + + + {{dash.revision}} + + + {{dash.installedRevision}} - diff --git a/public/app/features/datasources/edit_ctrl.ts b/public/app/features/datasources/edit_ctrl.ts index b766d6cda84..3ad74c628ab 100644 --- a/public/app/features/datasources/edit_ctrl.ts +++ b/public/app/features/datasources/edit_ctrl.ts @@ -54,7 +54,7 @@ export class DataSourceEditCtrl { return this.$q.when(null); } - return this.backendSrv.get('/api/org/plugins', {enabled: 1, type: 'datasource'}).then(plugins => { + return this.backendSrv.get('/api/plugins', {enabled: 1, type: 'datasource'}).then(plugins => { datasourceTypes = plugins; this.types = plugins; }); @@ -70,7 +70,7 @@ export class DataSourceEditCtrl { typeChanged() { this.hasDashboards = false; - return this.backendSrv.get('/api/org/plugins/' + this.current.type + '/settings').then(pluginInfo => { + return this.backendSrv.get('/api/plugins/' + this.current.type + '/settings').then(pluginInfo => { this.datasourceMeta = pluginInfo; this.hasDashboards = _.findWhere(pluginInfo.includes, {type: 'dashboard'}); }); diff --git a/public/app/features/plugins/edit_ctrl.ts b/public/app/features/plugins/edit_ctrl.ts index f8e7c96792c..2d4362835d4 100644 --- a/public/app/features/plugins/edit_ctrl.ts +++ b/public/app/features/plugins/edit_ctrl.ts @@ -22,7 +22,7 @@ export class PluginEditCtrl { } init() { - return this.backendSrv.get(`/api/org/plugins/${this.pluginId}/settings`).then(result => { + return this.backendSrv.get(`/api/plugins/${this.pluginId}/settings`).then(result => { this.model = result; this.pluginIcon = this.getPluginIcon(this.model.type); diff --git a/public/app/features/plugins/list_ctrl.ts b/public/app/features/plugins/list_ctrl.ts index a1bacc09c14..2693a4d8755 100644 --- a/public/app/features/plugins/list_ctrl.ts +++ b/public/app/features/plugins/list_ctrl.ts @@ -8,7 +8,7 @@ export class PluginListCtrl { /** @ngInject */ constructor(private backendSrv: any) { - this.backendSrv.get('api/org/plugins').then(plugins => { + this.backendSrv.get('api/plugins', {embedded: 0}).then(plugins => { this.plugins = plugins; }); } From 4f52bc138fed3f2b378cb98452b389040c4a5e2e Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 11 Mar 2016 10:06:44 +0100 Subject: [PATCH 14/20] docs(examples): move app example into new repo --- examples/README.md | 3 +- examples/nginx-app/.gitignore | 7 --- examples/nginx-app/.jscs.json | 13 ----- examples/nginx-app/.jshintrc | 36 ------------ examples/nginx-app/Gruntfile.js | 54 ------------------ examples/nginx-app/package.json | 37 ------------ examples/nginx-app/plugin.json | 50 ---------------- examples/nginx-app/readme.md | 7 --- examples/nginx-app/src/components/config.html | 3 - examples/nginx-app/src/components/config.js | 6 -- examples/nginx-app/src/components/logs.html | 3 - examples/nginx-app/src/components/logs.js | 6 -- examples/nginx-app/src/components/stream.html | 3 - examples/nginx-app/src/components/stream.js | 6 -- examples/nginx-app/src/css/dark.css | 0 examples/nginx-app/src/css/light.css | 0 .../nginx-app/src/dashboards/dashboard.js | 17 ------ .../dashboards/nginx_connection_stats.json | 0 .../nginx-app/src/datasource/datasource.js | 12 ---- examples/nginx-app/src/datasource/module.js | 5 -- examples/nginx-app/src/datasource/plugin.json | 5 -- examples/nginx-app/src/img/logo_large.png | Bin 14460 -> 0 bytes examples/nginx-app/src/img/logo_small.png | Bin 6430 -> 0 bytes examples/nginx-app/src/module.js | 9 --- examples/nginx-app/src/panel/module.js | 15 ----- examples/nginx-app/src/panel/plugin.json | 5 -- 26 files changed, 2 insertions(+), 300 deletions(-) delete mode 100644 examples/nginx-app/.gitignore delete mode 100644 examples/nginx-app/.jscs.json delete mode 100644 examples/nginx-app/.jshintrc delete mode 100644 examples/nginx-app/Gruntfile.js delete mode 100644 examples/nginx-app/package.json delete mode 100644 examples/nginx-app/plugin.json delete mode 100644 examples/nginx-app/readme.md delete mode 100644 examples/nginx-app/src/components/config.html delete mode 100644 examples/nginx-app/src/components/config.js delete mode 100644 examples/nginx-app/src/components/logs.html delete mode 100644 examples/nginx-app/src/components/logs.js delete mode 100644 examples/nginx-app/src/components/stream.html delete mode 100644 examples/nginx-app/src/components/stream.js delete mode 100644 examples/nginx-app/src/css/dark.css delete mode 100644 examples/nginx-app/src/css/light.css delete mode 100644 examples/nginx-app/src/dashboards/dashboard.js delete mode 100644 examples/nginx-app/src/dashboards/nginx_connection_stats.json delete mode 100644 examples/nginx-app/src/datasource/datasource.js delete mode 100644 examples/nginx-app/src/datasource/module.js delete mode 100644 examples/nginx-app/src/datasource/plugin.json delete mode 100644 examples/nginx-app/src/img/logo_large.png delete mode 100644 examples/nginx-app/src/img/logo_small.png delete mode 100644 examples/nginx-app/src/module.js delete mode 100644 examples/nginx-app/src/panel/module.js delete mode 100644 examples/nginx-app/src/panel/plugin.json diff --git a/examples/README.md b/examples/README.md index 04a544be399..64341a7fdbc 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,3 +1,4 @@ ## Example plugin implementations -[simple-json-datasource](https://github.com/grafana/simple-json-datasource) \ No newline at end of file +datasource:[simple-json-datasource](https://github.com/grafana/simple-json-datasource) +app: [example-app](https://github.com/grafana/example-app) \ No newline at end of file diff --git a/examples/nginx-app/.gitignore b/examples/nginx-app/.gitignore deleted file mode 100644 index 8c2c350441b..00000000000 --- a/examples/nginx-app/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.DS_Store - -node_modules -tmp/* -npm-debug.log -dist/* - diff --git a/examples/nginx-app/.jscs.json b/examples/nginx-app/.jscs.json deleted file mode 100644 index dcf694dcc63..00000000000 --- a/examples/nginx-app/.jscs.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "disallowImplicitTypeConversion": ["string"], - "disallowKeywords": ["with"], - "disallowMultipleLineBreaks": true, - "disallowMixedSpacesAndTabs": true, - "disallowTrailingWhitespace": true, - "requireSpacesInFunctionExpression": { - "beforeOpeningCurlyBrace": true - }, - "disallowSpacesInsideArrayBrackets": true, - "disallowSpacesInsideParentheses": true, - "validateIndentation": 2 -} \ No newline at end of file diff --git a/examples/nginx-app/.jshintrc b/examples/nginx-app/.jshintrc deleted file mode 100644 index 3725af83afc..00000000000 --- a/examples/nginx-app/.jshintrc +++ /dev/null @@ -1,36 +0,0 @@ -{ - "browser": true, - "esnext": true, - - "bitwise":false, - "curly": true, - "eqnull": true, - "devel": true, - "eqeqeq": true, - "forin": false, - "immed": true, - "supernew": true, - "expr": true, - "indent": 2, - "latedef": true, - "newcap": true, - "noarg": true, - "noempty": true, - "undef": true, - "boss": true, - "trailing": true, - "laxbreak": true, - "laxcomma": true, - "sub": true, - "unused": true, - "maxdepth": 6, - "maxlen": 140, - - "globals": { - "System": true, - "define": true, - "require": true, - "Chromath": false, - "setImmediate": true - } -} diff --git a/examples/nginx-app/Gruntfile.js b/examples/nginx-app/Gruntfile.js deleted file mode 100644 index b88d857cde3..00000000000 --- a/examples/nginx-app/Gruntfile.js +++ /dev/null @@ -1,54 +0,0 @@ -module.exports = function(grunt) { - - require('load-grunt-tasks')(grunt); - - grunt.loadNpmTasks('grunt-execute'); - grunt.loadNpmTasks('grunt-contrib-clean'); - - grunt.initConfig({ - - clean: ["dist"], - - copy: { - src_to_dist: { - cwd: 'src', - expand: true, - src: ['**/*', '!**/*.js', '!**/*.scss'], - dest: 'dist' - }, - pluginDef: { - expand: true, - src: ['plugin.json', 'readme.md'], - dest: 'dist', - } - }, - - watch: { - rebuild_all: { - files: ['src/**/*', 'plugin.json', 'readme.md'], - tasks: ['default'], - options: {spawn: false} - }, - }, - - babel: { - options: { - sourceMap: true, - presets: ["es2015"], - plugins: ['transform-es2015-modules-systemjs', "transform-es2015-for-of"], - }, - dist: { - files: [{ - cwd: 'src', - expand: true, - src: ['**/*.js'], - dest: 'dist', - ext:'.js' - }] - }, - }, - - }); - - grunt.registerTask('default', ['clean', 'copy:src_to_dist', 'copy:pluginDef', 'babel']); -}; diff --git a/examples/nginx-app/package.json b/examples/nginx-app/package.json deleted file mode 100644 index 91c53734ec8..00000000000 --- a/examples/nginx-app/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "kentik-app", - "private": true, - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/raintank/kentik-app-poc.git" - }, - "author": "", - "license": "ISC", - "bugs": { - "url": "https://github.com/raintank/kentik-app-poc/issues" - }, - "devDependencies": { - "grunt": "~0.4.5", - "babel": "~6.5.1", - "grunt-babel": "~6.0.0", - "grunt-contrib-copy": "~0.8.2", - "grunt-contrib-watch": "^0.6.1", - "grunt-contrib-uglify": "~0.11.0", - "grunt-systemjs-builder": "^0.2.5", - "load-grunt-tasks": "~3.2.0", - "grunt-execute": "~0.2.2", - "grunt-contrib-clean": "~0.6.0" - }, - "dependencies": { - "babel-plugin-transform-es2015-modules-systemjs": "^6.5.0", - "babel-preset-es2015": "^6.5.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 deleted file mode 100644 index 65f01ad62fc..00000000000 --- a/examples/nginx-app/plugin.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "type": "app", - "name": "Nginx", - "id": "nginx-app", - - "staticRoot": ".", - - "pages": [ - { "name": "Live stream", "component": "StreamPageCtrl", "role": "Editor"}, - { "name": "Log view", "component": "LogsPageCtrl", "role": "Viewer"} - ], - - "css": { - "dark": "css/dark.css", - "light": "css/light.css" - }, - - "info": { - "description": "Official Grafana Nginx App & Dashboard bundle", - "author": { - "name": "Nginx Inc.", - "url": "http://nginx.com" - }, - "keywords": ["nginx"], - "logos": { - "small": "img/logo_small.png", - "large": "img/logo_large.png" - }, - "links": [ - {"name": "Project site", "url": "http://project.com"}, - {"name": "License & Terms", "url": "http://license.com"} - ], - "version": "1.0.0", - "updated": "2015-02-10" - }, - - "includes": [ - {"type": "dashboard", "name": "Nginx Connection stats", "path": "dashboards/nginx_connection_stats.json"}, - {"type": "panel", "name": "Nginx Panel"}, - {"type": "datasource", "name": "Nginx Datasource"} - ], - - "dependencies": { - "grafanaVersion": "3.x.x", - "plugins": [ - {"type": "datasource", "id": "graphite", "name": "Graphite", "version": "1.0.0"}, - {"type": "panel", "id": "graph", "name": "Graph", "version": "1.0.0"} - ] - } -} diff --git a/examples/nginx-app/readme.md b/examples/nginx-app/readme.md deleted file mode 100644 index c267cfbb1fc..00000000000 --- a/examples/nginx-app/readme.md +++ /dev/null @@ -1,7 +0,0 @@ -## Overview - -This application is an example app. - -### Awesome - -Even though it does not have any features it is still pretty awesome. diff --git a/examples/nginx-app/src/components/config.html b/examples/nginx-app/src/components/config.html deleted file mode 100644 index c531ec36d76..00000000000 --- a/examples/nginx-app/src/components/config.html +++ /dev/null @@ -1,3 +0,0 @@ -

- Nginx config! -

diff --git a/examples/nginx-app/src/components/config.js b/examples/nginx-app/src/components/config.js deleted file mode 100644 index bb8f007b9bc..00000000000 --- a/examples/nginx-app/src/components/config.js +++ /dev/null @@ -1,6 +0,0 @@ - -export class NginxAppConfigCtrl { -} -NginxAppConfigCtrl.templateUrl = 'components/config.html'; - - diff --git a/examples/nginx-app/src/components/logs.html b/examples/nginx-app/src/components/logs.html deleted file mode 100644 index ca215772bf5..00000000000 --- a/examples/nginx-app/src/components/logs.html +++ /dev/null @@ -1,3 +0,0 @@ -

- Logs page! -

diff --git a/examples/nginx-app/src/components/logs.js b/examples/nginx-app/src/components/logs.js deleted file mode 100644 index 5b67290381b..00000000000 --- a/examples/nginx-app/src/components/logs.js +++ /dev/null @@ -1,6 +0,0 @@ - -export class LogsPageCtrl { -} -LogsPageCtrl.templateUrl = 'components/logs.html'; - - diff --git a/examples/nginx-app/src/components/stream.html b/examples/nginx-app/src/components/stream.html deleted file mode 100644 index ad70ca4df50..00000000000 --- a/examples/nginx-app/src/components/stream.html +++ /dev/null @@ -1,3 +0,0 @@ -

- Stream page! -

diff --git a/examples/nginx-app/src/components/stream.js b/examples/nginx-app/src/components/stream.js deleted file mode 100644 index 8684b36c64d..00000000000 --- a/examples/nginx-app/src/components/stream.js +++ /dev/null @@ -1,6 +0,0 @@ - -export class StreamPageCtrl { -} -StreamPageCtrl.templateUrl = 'components/stream.html'; - - diff --git a/examples/nginx-app/src/css/dark.css b/examples/nginx-app/src/css/dark.css deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/examples/nginx-app/src/css/light.css b/examples/nginx-app/src/css/light.css deleted file mode 100644 index e69de29bb2d..00000000000 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/nginx_connection_stats.json b/examples/nginx-app/src/dashboards/nginx_connection_stats.json deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/examples/nginx-app/src/datasource/datasource.js b/examples/nginx-app/src/datasource/datasource.js deleted file mode 100644 index 7f4ed363707..00000000000 --- a/examples/nginx-app/src/datasource/datasource.js +++ /dev/null @@ -1,12 +0,0 @@ -export default class NginxDatasource { - - constructor() {} - - query(options) { - return []; - } - - testDatasource() { - return false; - } -} diff --git a/examples/nginx-app/src/datasource/module.js b/examples/nginx-app/src/datasource/module.js deleted file mode 100644 index a3473e59889..00000000000 --- a/examples/nginx-app/src/datasource/module.js +++ /dev/null @@ -1,5 +0,0 @@ -import {Datasource} from './datasource'; - -export { - Datasource -}; \ No newline at end of file diff --git a/examples/nginx-app/src/datasource/plugin.json b/examples/nginx-app/src/datasource/plugin.json deleted file mode 100644 index ffce9492418..00000000000 --- a/examples/nginx-app/src/datasource/plugin.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "type": "datasource", - "name": "Nginx Datasource", - "id": "nginx-datasource" -} diff --git a/examples/nginx-app/src/img/logo_large.png b/examples/nginx-app/src/img/logo_large.png deleted file mode 100644 index c28955960e4d842f8d47496e9382e7ceadae2100..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14460 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 TE(&}n2vU?&eO@MG67+un{$ko6 diff --git a/examples/nginx-app/src/img/logo_small.png b/examples/nginx-app/src/img/logo_small.png deleted file mode 100644 index a6040f66f3da060c679acd661cf8b1f53eb6720d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6430 zcmaJ`cUTk4+uej7X$lC^6|PhP=?GE|ARUz=0Vzsv2I&R} zQUjq&Z=sh^@&$kYf4k2FwiSZ)Dk)oKwvdq3Iy43z$8b!9RrSljW#AV_dnUO^>2Z1= z-LG`~0I&D*X*^&}Qap|<9V1-INsCC@cmvXZGtAVpVW&XZgzr+2r$Enh1Z3q!Rx*4^d6?0! zy8$We?5POfnFrqeEsWUz`sPvOP&*}g9mzwZ9i!@SKepRFakH^ z)qjOymTg(e!2cHS@_i-g>y)1TIj79wM+S>uIeoc_&bnO;Ud4UZ4`JQ8S1V`Ap#qu{ z<(cfdEFr0F0G-HOw%kWaVT!a9C-}tg4H7HL{VXmz_hh1t9J9=Zw93zgkg(_=rOj+v z67Q@XlTZn_@OogB!1YhVF<+Tsm*xYsZ<@rRb1q`95a zN)gJjJyFUk`BJb&&ZAkHsdCkhPF}%VNNDk+!a+)IImWHb73&)pr{#K=Cu_Ro@&e+x zk*mlpBs-@^8ALV}gO!ad8%eCf6 zmx0^#&h*h_E|P}8W;6!v8{2pLEk6l4f?uGNC!&fL%ic-TcrODC_2rzb(XD$}dwNf! zCbvka*F~(>LNC$ZpLa@%HR?B=fK5!_0~Q4eFN6C-lY(y7s$$Z=FhXFMN&@GcDbhqY z@Q(#z6@55xQx_EI`;)n&_3%u+Zfypke30WxI>A2O1WMK+Q=UB2EXDZGfU)P`Fup7%>?%Gu&c_B7z;Pn$qS)Cy3Paa|);4UJ3^g;L}{ou!odABQE zXvrc{cEa!DV=h`00=KijEGZbIVut_e>79^9cZkZqCDuSSyGQ z$f-mh^}L<}I~BT-%W1*om>yBA^}xT3#94?gssB-aB)(!KpG?_I*da#)=w0qzy<_cv z3jJ0x(ktpdc+))AMX@7jOM(&Th3Xu1h~6J)mmUXw9?}12puE_$o*MgZYGGQM37BaK zsJmS&N9RZ7nBOWurSL`rnl%d~GX0F4xF%N$Up3{dc;UWj8YAIQ0H!&oYFd1U@B4b99!sx1+p>rc>J2 z!0t#p^$@=JgIc!Hg|lr=C;u}(qaXo|Y+)%q;fVtfubrDP3WVpOZCl7&>-tGHnW0tG z*g$w>A3L|eF*ENtWq!|FI4$Po8>m8}Lwj^V7`8GyKni>!#WUHluxjqS&j9_T7px_5 z49&`_>W{r+?bGXS`E8K}^iEQu$A+~^Qzf>bjt(wjK45l917Jx}AZ#;PJ>B$s?Q@4a zEM}_pH%c*w`T!#n?8PEU9+;x&(%q|#ChRQP){zBDn(|7@aK_wan&6F9elRK>DC;_> zP5z*qC;)M=`J=@KR{=jpnlFNpzr-8a{cmouvTI&sPi`h>Xv2UPKb&9ctAA1c+FzjmSShq|;_QZGC<{5Ufc?dQZl9GD^UB6) zLTcXn3xk-Y#S;foD6=H!4=P1rS4evm^93e9Nt>_M&Tw?{Ce1i)cZfAoQz>&`Qwpc~ zQ~7h4^~2q)F;|_Zk(R4oJ2*mXYZ)RmMASb24O|%}79u0&Hto zYHKyuy6*`dLSa<3iI4(b={gaBO^lPI+Q7*IaHW{Owm8SA}wl6xaPzMB%Qt zS&QN+X!+RzrP_NO8Iyp&&<`u6oT!pvtk9DWlm5JFme$wP)4AM-pkvg2$#+i=&jT*a z(soQ{SLCY4xUA`dD=%oQ0s=+dN+mx%X?nBpmhP;S#eKb>k&|=Q`VLo;b)KkbK(1b7 zb#$5EnU*@0=yTbKhz9T1o0jwQMg*PG|GJ@$aq6HQ&Fa^fB(`{5PUX={Uru#24?3dK zy0wLR;Nhm^z#FUmIxj!mCrvjY9|VICaAde$0bq#RqvDn}wJH#zv>{2l1XO=MOX5 z=m}d=OoFJP?_DPLR)dQv^)jBL_g6}lga8CzisktJF%*c~ql4xNgf454HpgQqU zl>O0$K6Z=(&mE5p^yz0F5AEhN+?oldG&U))9=top9MEuU!Vwn^&i`+=Rm0<6D~-!^ zd~BA_kcab|l{N5opdV?>Iw6k6SIAo(#s-fOJUjhwW_%M(_iH zGI?aQeP2;iKX(nP)X1(A&EB#*q#8MjjDp%G)K7tv-*ai z>rX-?pMafGx+eWQqC7@dsG@ZF*XNR-8+lW(6ALA(*o|j-?lKsZ|IJt!N|I^h)iv0& z4|&80T-w$M7o0s^LC{}e2;@Wzm+3Ju5r?qgngTZkip7zqw}~NZTC~w?L%Ppaw|{;D z67uB4Dn#~0f~{ma8OdPQImABH*4tj02Pz#%9SMCvyX21nhvT)1n}zT1msE63IH8Ow(?_iq@H4o%;y7ypZH0K#yht zaau^nb({6=6`U4M^CgvZCkO!@s6&`=dFI0ubv7lflTR9i$Ju2MugEs$kE|W8u-kf%V~_{h@-1@IGX9$(X;ioh z)Qt2U@U{4vN-Blg-cOzf9@D!m2blXrG!{^q=qdb9@zG&{_t}V&=bj4Rsu(}eq0L3u zA!}KgTGGPed^k{`iL>>+d}2(tV*Z`63i#QThp#SBtrG4;m4Uc9fAuV^kK^p-Bba#c zYatVD^m#|Q5PPf_JCTZvzQ9SnE!6QpmN!_zkYfFSEXV30DVP$ec*Y!(|@ho{i*Oe5q7oY zb9|#X(ST+lH&V>#{19IhFm$f@FxRx$5Qr)k|JHEXKi_BhLwH_yCDOxu%=U(H>V_=Q ztwe@@wnB~Q>?Y>J4l1mFU2^_(fah;bav@7Y&O?Q{eLYQ*a^YC)^Ur^&9XdjIywBY1 zzt_1fQlF+52l1jO){KccJ=Jo2Fz9G2w_52e^r>WTX~mV0a4b5{2K<$nS;PKnXs#Cpb^chnObX)VAwy^?n=l<&X_{Vd+>gAa)6=#U@a1h;m z=d~IKBD1;ir0=taVh!oahx2CZAGYgg|CLwaSEj=3)!z(JIA;miFzs0Gl`y(G?N8}S zSMk5@08#0hN@3WQhaFQj+Baqn zS~E`H9#DD^QG`=aYZBy@SE0;Ga6>?wevN!gCQOKMu=2TMfc>p*owbjuL>}X< ztV~@jyH7=D%WR0ris#Rz(wmqGn&CuzQ8I~!KhxKnPq!ATPh^=)8b`hW3G2PTivPU3 z{4GRM66As$t)U@y#o-1bk$S5d4Z&rf0s0UR_-8&Q1U*sC4nTd`Oe_5R%jx|8fm=(& z{}2t-xdK-T(28oxzVjTv9yF>7;%;U9^>M!sUWPeO9UCR~BjfarQZ;n)-j&4vFGtCBAMZ56+# z8`x+c_gAQSw-fqz(FZRrPie0l1{>d`^<1b%nNSiPB*vmvuwx$hKDb~6kszmSnb`VQ zGyF`dpoB@pl$+`#Rnx0lubY(OHoM7vRu_6h&4tyaRm5e7^cLnrf={dHbY^hpwWT5N zWo|_*-rI00&Ov)ZT5P1B2^f89@vAW>Yhk7@G)`#fdvL}n`@@DNK7H=XLI<;clfNVB znBu!LTwlF(Rl}y{m@EE{i}tF`pxsaP+}6lFH!{>%(3i_H;EPCDZ(jfV*VjI$>4t9x z4a)*n(rM0}fj%$?+(*X=$(}wlU*fPl`wM5b*Hst0aD)%8Z+{=Fz1wJiknNeyw?$Z+ zra+2~WH12%w|TM@zvH4%+H}Fk!B#)K`jis{@|tzxWk>hwvX6&!6A}x9%K%KkhIXCD zDO?$fb;LRwum=uwNR{` zEzc4LQO!q(ftk7@2j1OY(N-MwbKz}_3i7aUHm7ZI$JC9XS7Y=MqJATJpe!3u!oLi2 z2DA){{^2Cu*2qtBdBz(RY2iYv=R>;L`%_Z%lqKCDJG=XYrlsnjM!tKwYwZj|&rj4u zWxOuU#W#KWPG3G19IyYS#P`W1mr_`#x82yHYu=mHQ@d1X)nip)1YNRExJ*v^B|c+O zmr{i)+C67}1AXgp_^xr9mIjfJNHV36EH)d~^OCKn(Hv8J`|rnQ>kITa?i?=D`mAFl zwPNolqK@Y?V#M0Gg$dPwR+{Y~E(qPA>pqiW#%U_`q0P;BJ2Ewvh_)h^4If>OJMBR^ zC!+)r;V{In$6{|iCX{@Ytj3ojlll#wg5mETyO7*kGn!o@<@c}(AyrPKB9DVre9|;v ze%*;4_|s!r;r22yetE$Mu40mZQ^oi0ph}Q(;HvhVs_=?!#SF~X3{+;rELdkFR}8lV z%FIIOf9QC?P?g#3+~-y4Z+PV*W@@R@NYt01)SM$J%0n1OVF$Nw%^8dGzqe&b7R7I{ zl(pV@wJ0?Q5#}q=XzV>!%ilC$LEQ?~=7F~2;|l5zktOB>a>jROC1p;*P=ie$!}*G; zZ19{jG!~y&!0F4KHcX%H3u4*iotD?RY^Zf%HGd!LGDAgeRGh@P!<*)5FWCHhkt zmt_V!>in!hZSj8rSKqRQQH2p&aTtj^y$Z&m8fBHJIJj0Y?^;jCS+=8?oW&Q`ATl8rG5+N+8 zzeJZfc}%j(+_Dk7GzHnDXwDB7aIa-ge`&5K8v1e1m>{<1sg_XLbPKfx&ISU%1`A3} zx5Hc$OB`f9_>xs7FggwMvh)aRK1>t|Vy^Z^dX}GNXC+bV2ZqOmjWv(6tMpkpo)T#%Es+ zPoW>}9uGpXAk3G`driODP;%g@p6nqH1E4^g9@-qXG}669e&~4`u35z%*4Nj&CXGHm zW<_p~Q*e^tOL1qDmdaeh4qv&HnnUug8H~^huL#b$Rc~S9TPgIqJqCa=d=2F#4Gcps zYE>iHxOHL$jU2e9o2U%Lj|HXm@($Pnvzsvf9BhRM;XBSo!&T(3b3AxYnSz&TafKJ{hfUVFs# zhg}di{`*2@6+21}vKqm;n=H<7zRE>ZqD=n`t(;y^3tEAU~eN&BcGprGQgi zAM7N3HaI(HA#c$rpCawMF@*^aFkv)5cT;#Jdv*9-t4^D~0j*W^5_`+*ta5(9j_v~Y zdl(0miv*b!4Jr^6h52H;6iWPCivUB0E+*t?Cp0Ub&z+^2%&X0#+DM-VD0yn8`9u9) zv67pZm}vCO^uPH4N-oUJobLy5KUiV#@+L|yqi6VC^Nf%43ptAG0nL-#5AAh{+rdm& zANC$Usl6zn2)`YXu00+V;g$K-o=4s4uYm;tY_{roh)y+7-&3JKJW8pYl)iKT+nGmx zlWoS|owE`y=6G=>U0%6y3UFy<_8*uwY)k3@5z^(co|LicWS3hpPmDQfs;f5qH8ad* zhWtFN1?!`b@dPh{5in69?ri~qf4L2V{Q?-OBqCnIBnka2=i iQH~SO|Mzpi_>yi4cUPrR^!_`5cxb8XJt|j)hW;Pzzc)Mp diff --git a/examples/nginx-app/src/module.js b/examples/nginx-app/src/module.js deleted file mode 100644 index b5aeecc6ccf..00000000000 --- a/examples/nginx-app/src/module.js +++ /dev/null @@ -1,9 +0,0 @@ -import {LogsPageCtrl} from './components/logs'; -import {StreamPageCtrl} from './components/stream'; -import {NginxAppConfigCtrl} from './components/config'; - -export { - NginxAppConfigCtrl as ConfigCtrl, - StreamPageCtrl, - LogsPageCtrl -}; diff --git a/examples/nginx-app/src/panel/module.js b/examples/nginx-app/src/panel/module.js deleted file mode 100644 index 899586da81b..00000000000 --- a/examples/nginx-app/src/panel/module.js +++ /dev/null @@ -1,15 +0,0 @@ -import {PanelCtrl} from 'app/plugins/sdk'; - -class NginxPanelCtrl extends PanelCtrl { - - constructor($scope, $injector) { - super($scope, $injector); - } - -} -NginxPanelCtrl.template = '

nginx!

'; - -export { - NginxPanelCtrl as PanelCtrl -}; - diff --git a/examples/nginx-app/src/panel/plugin.json b/examples/nginx-app/src/panel/plugin.json deleted file mode 100644 index f3548c987f3..00000000000 --- a/examples/nginx-app/src/panel/plugin.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "type": "panel", - "name": "Nginx Panel", - "id": "nginx-panel" -} From 01788e86e5b63e0508924e788ea1ef9866b790db Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Fri, 11 Mar 2016 18:14:29 +0900 Subject: [PATCH 15/20] (cloudwatch) add document for new query --- docs/sources/datasources/cloudwatch.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/sources/datasources/cloudwatch.md b/docs/sources/datasources/cloudwatch.md index 4d9d8b38c1c..351b08eb2aa 100644 --- a/docs/sources/datasources/cloudwatch.md +++ b/docs/sources/datasources/cloudwatch.md @@ -64,9 +64,19 @@ Name | Description `metrics(namespace)` | Returns a list of metrics in the namespace. `dimension_keys(namespace)` | Returns a list of dimension keys in the namespace. `dimension_values(region, namespace, metric, dimension_key)` | Returns a list of dimension values matching the specified `region`, `namespace`, `metric` and `dimension_key`. +`ebs_volume_ids(region, instance_id)` | Returns a list of volume id matching the specified `region`, `instance_id`. +`ec2_instance_attribute(region, attribute_name, filters)` | Returns a list of attribute matching the specified `region`, `attribute_name`, `filters`. For details about the metrics CloudWatch provides, please refer to the [CloudWatch documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html). +The `ec2_instance_attribute` query take `filters` in JSON format. +You can specify [pre-defined filters of ec2:DescribeInstances](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html). +Specify like `{ filter_name1: [ filter_value1 ], filter_name2: [ filter_value2 ] }` + +Example `ec2_instance_attribute()` query + + ec2_instance_attribute(us-east-1, InstanceId, { "tag:Environment": [ "production" ] }) + ![](/img/v2/cloudwatch_templating.png) ## Cost From fc54c01f01dc9e5136b65d8e477cfa2458d24043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 11 Mar 2016 12:31:56 +0100 Subject: [PATCH 16/20] feat(plugins): more work on plugin dashboard install, #4298 --- pkg/api/dtos/plugins.go | 7 +-- pkg/plugins/dashboard_installer.go | 3 +- pkg/plugins/dashboards.go | 6 ++- public/app/core/routes/routes.ts | 6 +-- public/app/features/dashboard/all.js | 1 - public/app/features/datasources/all.js | 4 -- public/app/features/org/all.js | 1 - public/app/features/plugins/all.ts | 9 ++-- .../edit_ctrl.ts => plugins/ds_edit_ctrl.ts} | 2 +- .../list_ctrl.ts => plugins/ds_list_ctrl.ts} | 0 .../plugins/import_list/import_list.html | 37 ++++++++++++++++ .../import_list/import_list.ts | 44 +++++-------------- .../partials/ds_edit.html} | 0 .../partials/ds_http_settings.html} | 0 .../partials/ds_list.html} | 0 .../partials/{edit.html => plugin_edit.html} | 0 .../partials/{list.html => plugin_list.html} | 0 .../partials/{page.html => plugin_page.html} | 0 .../{edit_ctrl.ts => plugin_edit_ctrl.ts} | 0 .../{list_ctrl.ts => plugin_list_ctrl.ts} | 0 .../{page_ctrl.ts => plugin_page_ctrl.ts} | 0 .../graphite/dashboards/carbon_stats.json | 2 +- .../plugins/datasource/graphite/plugin.json | 3 +- 23 files changed, 70 insertions(+), 55 deletions(-) delete mode 100644 public/app/features/datasources/all.js rename public/app/features/{datasources/edit_ctrl.ts => plugins/ds_edit_ctrl.ts} (97%) rename public/app/features/{datasources/list_ctrl.ts => plugins/ds_list_ctrl.ts} (100%) create mode 100644 public/app/features/plugins/import_list/import_list.html rename public/app/features/{dashboard => plugins}/import_list/import_list.ts (56%) rename public/app/features/{datasources/partials/edit.html => plugins/partials/ds_edit.html} (100%) rename public/app/features/{datasources/partials/http_settings.html => plugins/partials/ds_http_settings.html} (100%) rename public/app/features/{datasources/partials/list.html => plugins/partials/ds_list.html} (100%) rename public/app/features/plugins/partials/{edit.html => plugin_edit.html} (100%) rename public/app/features/plugins/partials/{list.html => plugin_list.html} (100%) rename public/app/features/plugins/partials/{page.html => plugin_page.html} (100%) rename public/app/features/plugins/{edit_ctrl.ts => plugin_edit_ctrl.ts} (100%) rename public/app/features/plugins/{list_ctrl.ts => plugin_list_ctrl.ts} (100%) rename public/app/features/plugins/{page_ctrl.ts => plugin_page_ctrl.ts} (100%) diff --git a/pkg/api/dtos/plugins.go b/pkg/api/dtos/plugins.go index 144b9b09755..5f3dac76296 100644 --- a/pkg/api/dtos/plugins.go +++ b/pkg/api/dtos/plugins.go @@ -27,7 +27,8 @@ type PluginListItem struct { } type InstallPluginDashboardCmd struct { - PluginId string `json:"pluginId"` - Path string `json:"path"` - Inputs map[string]interface{} `json:"inputs"` + PluginId string `json:"pluginId"` + Path string `json:"path"` + Reinstall bool `json:"reinstall"` + Inputs map[string]interface{} `json:"inputs"` } diff --git a/pkg/plugins/dashboard_installer.go b/pkg/plugins/dashboard_installer.go index 9f52b518d70..279ab209989 100644 --- a/pkg/plugins/dashboard_installer.go +++ b/pkg/plugins/dashboard_installer.go @@ -48,8 +48,9 @@ func InstallPluginDashboard(cmd *InstallPluginDashboardCommand) error { Title: dashboard.Title, Path: cmd.Path, Revision: dashboard.GetString("revision", "1.0"), - InstalledURI: "db/" + saveCmd.Result.Slug, + InstalledUri: "db/" + saveCmd.Result.Slug, InstalledRevision: dashboard.GetString("revision", "1.0"), + Installed: true, } return nil diff --git a/pkg/plugins/dashboards.go b/pkg/plugins/dashboards.go index 9acb64cec1b..1697ad808f2 100644 --- a/pkg/plugins/dashboards.go +++ b/pkg/plugins/dashboards.go @@ -12,7 +12,8 @@ import ( type PluginDashboardInfoDTO struct { PluginId string `json:"pluginId"` Title string `json:"title"` - InstalledURI string `json:"installedURI"` + Installed bool `json:"installed"` + InstalledUri string `json:"installedUri"` InstalledRevision string `json:"installedRevision"` Revision string `json:"revision"` Description string `json:"description"` @@ -83,7 +84,8 @@ func getDashboardImportStatus(orgId int64, plugin *PluginBase, path string) (*Pl return nil, err } } else { - res.InstalledURI = "db/" + query.Result.Slug + res.Installed = true + res.InstalledUri = "db/" + query.Result.Slug res.InstalledRevision = query.Result.GetString("revision", "1.0") } diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts index 91ef4cd8b25..5ec55c311e5 100644 --- a/public/app/core/routes/routes.ts +++ b/public/app/core/routes/routes.ts @@ -52,19 +52,19 @@ function setupAngularRoutes($routeProvider, $locationProvider) { templateUrl: 'public/app/features/datasources/partials/list.html', controller : 'DataSourcesCtrl', controllerAs: 'ctrl', - resolve: loadOrgBundle, + resolve: loadPluginsBundle, }) .when('/datasources/edit/:id', { templateUrl: 'public/app/features/datasources/partials/edit.html', controller : 'DataSourceEditCtrl', controllerAs: 'ctrl', - resolve: loadOrgBundle, + resolve: loadPluginsBundle, }) .when('/datasources/new', { templateUrl: 'public/app/features/datasources/partials/edit.html', controller : 'DataSourceEditCtrl', controllerAs: 'ctrl', - resolve: loadOrgBundle, + resolve: loadPluginsBundle, }) .when('/org', { templateUrl: 'public/app/features/org/partials/orgDetails.html', diff --git a/public/app/features/dashboard/all.js b/public/app/features/dashboard/all.js index b96f6693324..d110019add6 100644 --- a/public/app/features/dashboard/all.js +++ b/public/app/features/dashboard/all.js @@ -13,7 +13,6 @@ define([ './timeSrv', './unsavedChangesSrv', './timepicker/timepicker', - './import_list/import_list', './graphiteImportCtrl', './dynamicDashboardSrv', './importCtrl', diff --git a/public/app/features/datasources/all.js b/public/app/features/datasources/all.js deleted file mode 100644 index b181fd475c2..00000000000 --- a/public/app/features/datasources/all.js +++ /dev/null @@ -1,4 +0,0 @@ -define([ - './list_ctrl', - './edit_ctrl', -], function () {}); diff --git a/public/app/features/org/all.js b/public/app/features/org/all.js index cebd0dd1def..e04634d709a 100644 --- a/public/app/features/org/all.js +++ b/public/app/features/org/all.js @@ -4,5 +4,4 @@ define([ './userInviteCtrl', './orgApiKeysCtrl', './orgDetailsCtrl', - '../datasources/all', ], function () {}); diff --git a/public/app/features/plugins/all.ts b/public/app/features/plugins/all.ts index 9d54165e56b..346fb2b30ef 100644 --- a/public/app/features/plugins/all.ts +++ b/public/app/features/plugins/all.ts @@ -1,3 +1,6 @@ -import './edit_ctrl'; -import './page_ctrl'; -import './list_ctrl'; +import './plugin_edit_ctrl'; +import './plugin_page_ctrl'; +import './plugin_list_ctrl'; +import './import_list/import_list'; +import './ds_edit_ctrl'; +import './ds_list_ctrl'; diff --git a/public/app/features/datasources/edit_ctrl.ts b/public/app/features/plugins/ds_edit_ctrl.ts similarity index 97% rename from public/app/features/datasources/edit_ctrl.ts rename to public/app/features/plugins/ds_edit_ctrl.ts index 3ad74c628ab..1e07b3a814a 100644 --- a/public/app/features/datasources/edit_ctrl.ts +++ b/public/app/features/plugins/ds_edit_ctrl.ts @@ -140,6 +140,6 @@ coreModule.controller('DataSourceEditCtrl', DataSourceEditCtrl); coreModule.directive('datasourceHttpSettings', function() { return { scope: {current: "="}, - templateUrl: 'public/app/features/datasources/partials/http_settings.html' + templateUrl: 'public/app/features/plugins/partials/ds_http_settings.html' }; }); diff --git a/public/app/features/datasources/list_ctrl.ts b/public/app/features/plugins/ds_list_ctrl.ts similarity index 100% rename from public/app/features/datasources/list_ctrl.ts rename to public/app/features/plugins/ds_list_ctrl.ts diff --git a/public/app/features/plugins/import_list/import_list.html b/public/app/features/plugins/import_list/import_list.html new file mode 100644 index 00000000000..d6c1bb914ce --- /dev/null +++ b/public/app/features/plugins/import_list/import_list.html @@ -0,0 +1,37 @@ +
+ + + + + + + + + + +
+ + + + {{dash.title}} + + + {{dash.title}} + + + v{{dash.revision}} + + Installed v{{dash.installedRevision}} + + + + +
+
+ diff --git a/public/app/features/dashboard/import_list/import_list.ts b/public/app/features/plugins/import_list/import_list.ts similarity index 56% rename from public/app/features/dashboard/import_list/import_list.ts rename to public/app/features/plugins/import_list/import_list.ts index 7d88d7f9f0a..62145623fa2 100644 --- a/public/app/features/dashboard/import_list/import_list.ts +++ b/public/app/features/plugins/import_list/import_list.ts @@ -4,9 +4,6 @@ import angular from 'angular'; import _ from 'lodash'; import coreModule from 'app/core/core_module'; -class DashboardScriptLoader { -} - export class DashImportListCtrl { dashboards: any[]; plugin: any; @@ -19,50 +16,32 @@ export class DashImportListCtrl { }); } - import(dash) { + import(dash, reinstall) { var installCmd = { pluginId: this.plugin.id, path: dash.path, + reinstall: reinstall, inputs: {} }; this.backendSrv.post(`/api/plugins/dashboards/install`, installCmd).then(res => { - console.log(res); + this.$rootScope.appEvent('alert-success', ['Dashboard Installed', dash.title]); + _.extend(dash, res); }); } + remove(dash) { + this.backendSrv.delete('/api/dashboards/' + dash.installedUri).then(() => { + this.$rootScope.appEvent('alert-success', ['Dashboard Deleted', dash.title]); + dash.installed = false; + }); + } } -var template = ` -
- - - - - - - - - - -
- - - {{dash.title}} - - {{dash.revision}} - - {{dash.installedRevision}} - - -
-
-`; - export function dashboardImportList() { return { restrict: 'E', - template: template, + templateUrl: 'public/app/features/plugins/import_list/import_list.html', controller: DashImportListCtrl, bindToController: true, controllerAs: 'ctrl', @@ -72,7 +51,6 @@ export function dashboardImportList() { }; } - coreModule.directive('dashboardImportList', dashboardImportList); diff --git a/public/app/features/datasources/partials/edit.html b/public/app/features/plugins/partials/ds_edit.html similarity index 100% rename from public/app/features/datasources/partials/edit.html rename to public/app/features/plugins/partials/ds_edit.html diff --git a/public/app/features/datasources/partials/http_settings.html b/public/app/features/plugins/partials/ds_http_settings.html similarity index 100% rename from public/app/features/datasources/partials/http_settings.html rename to public/app/features/plugins/partials/ds_http_settings.html diff --git a/public/app/features/datasources/partials/list.html b/public/app/features/plugins/partials/ds_list.html similarity index 100% rename from public/app/features/datasources/partials/list.html rename to public/app/features/plugins/partials/ds_list.html diff --git a/public/app/features/plugins/partials/edit.html b/public/app/features/plugins/partials/plugin_edit.html similarity index 100% rename from public/app/features/plugins/partials/edit.html rename to public/app/features/plugins/partials/plugin_edit.html diff --git a/public/app/features/plugins/partials/list.html b/public/app/features/plugins/partials/plugin_list.html similarity index 100% rename from public/app/features/plugins/partials/list.html rename to public/app/features/plugins/partials/plugin_list.html diff --git a/public/app/features/plugins/partials/page.html b/public/app/features/plugins/partials/plugin_page.html similarity index 100% rename from public/app/features/plugins/partials/page.html rename to public/app/features/plugins/partials/plugin_page.html diff --git a/public/app/features/plugins/edit_ctrl.ts b/public/app/features/plugins/plugin_edit_ctrl.ts similarity index 100% rename from public/app/features/plugins/edit_ctrl.ts rename to public/app/features/plugins/plugin_edit_ctrl.ts diff --git a/public/app/features/plugins/list_ctrl.ts b/public/app/features/plugins/plugin_list_ctrl.ts similarity index 100% rename from public/app/features/plugins/list_ctrl.ts rename to public/app/features/plugins/plugin_list_ctrl.ts diff --git a/public/app/features/plugins/page_ctrl.ts b/public/app/features/plugins/plugin_page_ctrl.ts similarity index 100% rename from public/app/features/plugins/page_ctrl.ts rename to public/app/features/plugins/plugin_page_ctrl.ts diff --git a/public/app/plugins/datasource/graphite/dashboards/carbon_stats.json b/public/app/plugins/datasource/graphite/dashboards/carbon_stats.json index 642f32b6e6a..ad2c32be759 100644 --- a/public/app/plugins/datasource/graphite/dashboards/carbon_stats.json +++ b/public/app/plugins/datasource/graphite/dashboards/carbon_stats.json @@ -6,7 +6,7 @@ } }, - "title": "Carbon stats", + "title": "Carbon Cache Stats", "version": 1, "rows": [ { diff --git a/public/app/plugins/datasource/graphite/plugin.json b/public/app/plugins/datasource/graphite/plugin.json index c923cb79c4e..f7715f9c5b9 100644 --- a/public/app/plugins/datasource/graphite/plugin.json +++ b/public/app/plugins/datasource/graphite/plugin.json @@ -4,8 +4,7 @@ "id": "graphite", "includes": [ - {"type": "dashboard", "name": "Carbon Overview", "path": "dashboards/carbon_stats.json"}, - {"type": "dashboard", "name": "Carbon Agent Details", "path": "dashboards/carbon_stats.json"} + {"type": "dashboard", "name": "Carbon Cache Stats", "path": "dashboards/carbon_stats.json"} ], "metrics": true, From d2b0bad1cffa0df4a2ea59c4064c2f57a8e8e542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 11 Mar 2016 13:02:21 +0100 Subject: [PATCH 17/20] fix(): fixed changed partials paths --- public/app/core/routes/routes.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/public/app/core/routes/routes.ts b/public/app/core/routes/routes.ts index 5ec55c311e5..92cf32448a1 100644 --- a/public/app/core/routes/routes.ts +++ b/public/app/core/routes/routes.ts @@ -49,19 +49,19 @@ function setupAngularRoutes($routeProvider, $locationProvider) { controller : 'DashboardImportCtrl', }) .when('/datasources', { - templateUrl: 'public/app/features/datasources/partials/list.html', + templateUrl: 'public/app/features/plugins/partials/ds_list.html', controller : 'DataSourcesCtrl', controllerAs: 'ctrl', resolve: loadPluginsBundle, }) .when('/datasources/edit/:id', { - templateUrl: 'public/app/features/datasources/partials/edit.html', + templateUrl: 'public/app/features/plugins/partials/ds_edit.html', controller : 'DataSourceEditCtrl', controllerAs: 'ctrl', resolve: loadPluginsBundle, }) .when('/datasources/new', { - templateUrl: 'public/app/features/datasources/partials/edit.html', + templateUrl: 'public/app/features/plugins/partials/ds_edit.html', controller : 'DataSourceEditCtrl', controllerAs: 'ctrl', resolve: loadPluginsBundle, @@ -168,19 +168,19 @@ function setupAngularRoutes($routeProvider, $locationProvider) { controllerAs: 'ctrl', }) .when('/plugins', { - templateUrl: 'public/app/features/plugins/partials/list.html', + templateUrl: 'public/app/features/plugins/partials/plugin_list.html', controller: 'PluginListCtrl', controllerAs: 'ctrl', resolve: loadPluginsBundle, }) .when('/plugins/:pluginId/edit', { - templateUrl: 'public/app/features/plugins/partials/edit.html', + templateUrl: 'public/app/features/plugins/partials/plugin_edit.html', controller: 'PluginEditCtrl', controllerAs: 'ctrl', resolve: loadPluginsBundle, }) .when('/plugins/:pluginId/page/:slug', { - templateUrl: 'public/app/features/plugins/partials/page.html', + templateUrl: 'public/app/features/plugins/partials/plugin_page.html', controller: 'AppPageCtrl', controllerAs: 'ctrl', resolve: loadPluginsBundle, From 8da702c2e7977fdf6d8bf357adf657afd1879762 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 11 Mar 2016 13:59:09 +0100 Subject: [PATCH 18/20] feat(cli): add grafana net url --- pkg/cmd/grafana-cli/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/grafana-cli/main.go b/pkg/cmd/grafana-cli/main.go index 4e74578c604..b277714fe9b 100644 --- a/pkg/cmd/grafana-cli/main.go +++ b/pkg/cmd/grafana-cli/main.go @@ -41,7 +41,7 @@ func main() { cli.StringFlag{ Name: "repo", Usage: "url to the plugin repository", - Value: "", + Value: "https://grafana-net.raintank.io/api/plugins", }, cli.BoolFlag{ Name: "debug, d", From f5bb2b11e5d8afe3d06bd542bde1496bc70f892f Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 11 Mar 2016 14:11:25 +0100 Subject: [PATCH 19/20] feat(cli): improve error handling for missing plugin dir --- pkg/cmd/grafana-cli/commands/commands.go | 1 + pkg/cmd/grafana-cli/commands/install_command.go | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/grafana-cli/commands/commands.go b/pkg/cmd/grafana-cli/commands/commands.go index 4993d8aa045..f1b36c90ef2 100644 --- a/pkg/cmd/grafana-cli/commands/commands.go +++ b/pkg/cmd/grafana-cli/commands/commands.go @@ -11,6 +11,7 @@ func runCommand(command func(commandLine CommandLine) error) func(context *cli.C cmd := &contextCommandLine{context} if err := command(cmd); err != nil { + log.Error("\nError: ") log.Errorf("%s\n\n", err) cmd.ShowHelp() diff --git a/pkg/cmd/grafana-cli/commands/install_command.go b/pkg/cmd/grafana-cli/commands/install_command.go index 4dfbfd80e31..9b9faaa3fe4 100644 --- a/pkg/cmd/grafana-cli/commands/install_command.go +++ b/pkg/cmd/grafana-cli/commands/install_command.go @@ -28,7 +28,15 @@ func validateInput(c CommandLine, pluginFolder string) error { } fileInfo, err := os.Stat(pluginDir) - if err != nil && !fileInfo.IsDir() { + if err != nil { + if err = os.MkdirAll(pluginDir, os.ModePerm); err != nil { + return errors.New("path is not a directory") + } + + return nil + } + + if !fileInfo.IsDir() { return errors.New("path is not a directory") } From 5094c1db2a970293fc788257caa7c0e8ea203a82 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 11 Mar 2016 14:34:48 +0100 Subject: [PATCH 20/20] feat(cli): improves error message for 401 requests --- pkg/cmd/grafana-cli/services/services.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/grafana-cli/services/services.go b/pkg/cmd/grafana-cli/services/services.go index fc77570b77c..cd03f755075 100644 --- a/pkg/cmd/grafana-cli/services/services.go +++ b/pkg/cmd/grafana-cli/services/services.go @@ -3,6 +3,7 @@ package services import ( "encoding/json" "errors" + "fmt" "github.com/franela/goreq" "github.com/grafana/grafana/pkg/cmd/grafana-cli/log" m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models" @@ -12,8 +13,12 @@ import ( var IoHelper m.IoUtil = IoUtilImp{} func ListAllPlugins(repoUrl string) (m.PluginRepo, error) { + fullUrl := repoUrl + "/repo" + res, _ := goreq.Request{Uri: fullUrl, MaxRedirects: 3}.Do() - res, _ := goreq.Request{Uri: repoUrl + "/repo", MaxRedirects: 3}.Do() + if res.StatusCode != 200 { + return m.PluginRepo{}, fmt.Errorf("Could not access %s statuscode %v", fullUrl, res.StatusCode) + } var resp m.PluginRepo err := res.Body.FromJsonTo(&resp)