From 36e99ac531651dac15cefa7e303294c262670094 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 19 Jan 2016 21:46:43 +0100 Subject: [PATCH 01/27] feat(tech): force create symlinks --- symlink_git_hooks.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/symlink_git_hooks.sh b/symlink_git_hooks.sh index 7c3b58feaca..c8a90432a70 100755 --- a/symlink_git_hooks.sh +++ b/symlink_git_hooks.sh @@ -1,3 +1,3 @@ #/bin/bash -ln -s .hooks/* .git/hooks/ +ln -s -f .hooks/* .git/hooks/ From c4a0ec844c242c8a890631b020d522dce59164fd Mon Sep 17 00:00:00 2001 From: Anthony Woods Date: Wed, 20 Jan 2016 06:13:45 +0800 Subject: [PATCH 02/27] get apiPlugins working again. --- pkg/plugins/api_plugin.go | 31 +++++++++++++++++++++++++++++++ pkg/plugins/models.go | 9 +++++++++ 2 files changed, 40 insertions(+) create mode 100644 pkg/plugins/api_plugin.go diff --git a/pkg/plugins/api_plugin.go b/pkg/plugins/api_plugin.go new file mode 100644 index 00000000000..b4296479ec1 --- /dev/null +++ b/pkg/plugins/api_plugin.go @@ -0,0 +1,31 @@ +package plugins + +import ( + "encoding/json" + "strings" + + "github.com/grafana/grafana/pkg/models" +) + +type ApiPluginRoute struct { + Path string `json:"path"` + Method string `json:"method"` + ReqSignedIn bool `json:"reqSignedIn"` + ReqGrafanaAdmin bool `json:"reqGrafanaAdmin"` + ReqRole models.RoleType `json:"reqRole"` + Url string `json:"url"` +} + +type ApiPlugin struct { + PluginBase + Routes []*ApiPluginRoute `json:"routes"` +} + +func (app *ApiPlugin) Load(decoder *json.Decoder, pluginDir string) error { + if err := decoder.Decode(&app); err != nil { + return err + } + + ApiPlugins[app.Id] = app + return nil +} diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index 375bdf1a52c..69230cac629 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -73,3 +73,12 @@ func NewEnabledPlugins() EnabledPlugins { Apps: make([]*AppPlugin, 0), } } + +func (app *ApiPlugin) Load(decoder *json.Decoder, pluginDir string) error { + if err := decoder.Decode(&app); err != nil { + return err + } + + ApiPlugins[app.Id] = app + return nil +} From 92a085550e122e80470b3fb2ca5a631db77a0dc8 Mon Sep 17 00:00:00 2001 From: Anthony Woods Date: Wed, 20 Jan 2016 06:17:48 +0800 Subject: [PATCH 03/27] fix up imports --- pkg/plugins/api_plugin.go | 1 - pkg/plugins/models.go | 25 ------------------------- 2 files changed, 26 deletions(-) diff --git a/pkg/plugins/api_plugin.go b/pkg/plugins/api_plugin.go index b4296479ec1..0a47b7d18b2 100644 --- a/pkg/plugins/api_plugin.go +++ b/pkg/plugins/api_plugin.go @@ -2,7 +2,6 @@ package plugins import ( "encoding/json" - "strings" "github.com/grafana/grafana/pkg/models" ) diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index 69230cac629..b302181e840 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -2,8 +2,6 @@ package plugins import ( "encoding/json" - - "github.com/grafana/grafana/pkg/models" ) type PluginLoader interface { @@ -44,20 +42,6 @@ type PluginStaticRoute struct { PluginId string } -type ApiPluginRoute struct { - Path string `json:"path"` - Method string `json:"method"` - ReqSignedIn bool `json:"reqSignedIn"` - ReqGrafanaAdmin bool `json:"reqGrafanaAdmin"` - ReqRole models.RoleType `json:"reqRole"` - Url string `json:"url"` -} - -type ApiPlugin struct { - PluginBase - Routes []*ApiPluginRoute `json:"routes"` -} - type EnabledPlugins struct { Panels []*PanelPlugin DataSources map[string]*DataSourcePlugin @@ -73,12 +57,3 @@ func NewEnabledPlugins() EnabledPlugins { Apps: make([]*AppPlugin, 0), } } - -func (app *ApiPlugin) Load(decoder *json.Decoder, pluginDir string) error { - if err := decoder.Decode(&app); err != nil { - return err - } - - ApiPlugins[app.Id] = app - return nil -} From 1c5be9225981569471767b91f8913c32ae7653c6 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 19 Jan 2016 17:15:24 -0800 Subject: [PATCH 04/27] Removed unused component from playlist code --- public/app/features/playlist/partials/playlist-remove.html | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 public/app/features/playlist/partials/playlist-remove.html diff --git a/public/app/features/playlist/partials/playlist-remove.html b/public/app/features/playlist/partials/playlist-remove.html deleted file mode 100644 index 8474f97f8de..00000000000 --- a/public/app/features/playlist/partials/playlist-remove.html +++ /dev/null @@ -1,5 +0,0 @@ -

Are you sure want to delete "{{playlist.title}}" playlist?

-

- - -

From d0074b25db989f583ee1b03e42cc2765cd6781a1 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Wed, 20 Jan 2016 18:30:17 +0900 Subject: [PATCH 05/27] (prometheus) add explanation about incompatibility of metric find query --- docs/sources/datasources/prometheus.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sources/datasources/prometheus.md b/docs/sources/datasources/prometheus.md index 61f6c0d66e8..a7959a41c51 100644 --- a/docs/sources/datasources/prometheus.md +++ b/docs/sources/datasources/prometheus.md @@ -51,6 +51,8 @@ Name | Description For details of `metric names` & `label names`, and `label values`, please refer to the [Prometheus documentation](http://prometheus.io/docs/concepts/data_model/#metric-names-and-labels). +> Note: The part of queries is incompatible with the version before 2.6, if you specify like `foo.*`, please change like `metrics(foo.*)`. + You can create a template variable in Grafana and have that variable filled with values from any Prometheus metric exploration query. You can then use this variable in your Prometheus metric queries. From 4f7f03a28c4d8aed480429ce62dd8158d78f172d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 20 Jan 2016 10:39:58 +0100 Subject: [PATCH 06/27] fix(playlist): fix for memory leak when running playlist for a long period, fixes #3794 --- CHANGELOG.md | 7 +++++-- public/app/features/dashlinks/module.js | 2 +- public/app/features/playlist/playlist_srv.ts | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cb01152d58..e97ab145c61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # 3.0.0 (unrelased master branch) -### New Features ### +### New Features * **Playlists**: Playlists can now be persisted and started from urls, closes [#3655](https://github.com/grafana/grafana/pull/3655) * **Metadata**: Settings panel now shows dashboard metadata, closes [#3304](https://github.com/grafana/grafana/issues/3304) * **InfluxDB**: Support for policy selection in query editor, closes [#2018](https://github.com/grafana/grafana/issues/2018) @@ -10,11 +10,14 @@ **InfluxDB 0.8.x** The data source for the old version of influxdb (0.8.x) is no longer included in default builds. Can easily be installed via improved plugin system, closes #3523 **KairosDB** The data source is no longer included in default builds. Can easily be installed via improved plugin system, closes #3524 -### Enhancements ### +### Enhancements * **Sessions**: Support for memcached as session storage, closes [#3458](https://github.com/grafana/grafana/pull/3458) * **mysql**: Grafana now supports ssl for mysql, closes [#3584](https://github.com/grafana/grafana/pull/3584) * **snapshot**: Annotations are now included in snapshots, closes [#3635](https://github.com/grafana/grafana/pull/3635) +### Bug fixes +* **Playlist**: Fix for memory leak when running a playlist, closes [#3794](https://github.com/grafana/grafana/pull/3794) + # 2.6.1 (unrelased, 2.6.x branch) ### New Features diff --git a/public/app/features/dashlinks/module.js b/public/app/features/dashlinks/module.js index b7029e6cf2d..f133290ceb2 100644 --- a/public/app/features/dashlinks/module.js +++ b/public/app/features/dashlinks/module.js @@ -159,7 +159,7 @@ function (angular, _) { }; updateDashLinks(); - $rootScope.onAppEvent('dash-links-updated', updateDashLinks, $rootScope); + $rootScope.onAppEvent('dash-links-updated', updateDashLinks, $scope); }); module.controller('DashLinkEditorCtrl', function($scope, $rootScope) { diff --git a/public/app/features/playlist/playlist_srv.ts b/public/app/features/playlist/playlist_srv.ts index 0570a3bfc85..8c6be5edaf5 100644 --- a/public/app/features/playlist/playlist_srv.ts +++ b/public/app/features/playlist/playlist_srv.ts @@ -1,6 +1,7 @@ /// import angular from 'angular'; +import config from 'app/core/config'; import coreModule from '../../core/core_module'; import kbn from 'app/core/utils/kbn'; @@ -20,10 +21,9 @@ class PlaylistSrv { var playedAllDashboards = this.index > this.dashboards.length - 1; if (playedAllDashboards) { - this.start(this.playlistId); + window.location.href = `${config.appSubUrl}/playlists/play/${this.playlistId}`; } else { var dash = this.dashboards[this.index]; - this.$location.url('dashboard/' + dash.uri); this.index++; From 1979143e7c3cb9b92c5e4b637fe783a4eaae3c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 20 Jan 2016 11:20:15 +0100 Subject: [PATCH 07/27] fix(export): fix export dashboard to json, been broken in master for 2-3 weeks --- public/app/grafana.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/grafana.ts b/public/app/grafana.ts index 7316a8164bc..d9ad3a449d3 100644 --- a/public/app/grafana.ts +++ b/public/app/grafana.ts @@ -1,6 +1,7 @@ /// import 'bootstrap'; +import 'vendor/filesaver'; import 'lodash-src'; import 'angular-strap'; import 'angular-route'; From 5f5fcc0e04d237f89b6f5a4b43fbb33c2e0c9282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 20 Jan 2016 11:57:56 +0100 Subject: [PATCH 08/27] fix(submenu): added ngInject comment to dashboard submenu --- public/app/features/dashboard/submenu/submenu.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/features/dashboard/submenu/submenu.ts b/public/app/features/dashboard/submenu/submenu.ts index 7e304d88464..75f0778b01f 100644 --- a/public/app/features/dashboard/submenu/submenu.ts +++ b/public/app/features/dashboard/submenu/submenu.ts @@ -7,6 +7,7 @@ export class SubmenuCtrl { variables: any; dashboard: any; + /** @ngInject */ constructor(private $rootScope, private templateValuesSrv, private dynamicDashboardSrv) { this.annotations = this.dashboard.templating.list; this.variables = this.dashboard.templating.list; From 9e121ef0c84cec7b55e5830e2d1a79c64a99ce32 Mon Sep 17 00:00:00 2001 From: Anthony Woods Date: Thu, 21 Jan 2016 02:21:30 +0800 Subject: [PATCH 09/27] add save button to config page configView directives can update the appModel object. We need a save button to persist the updates. --- public/app/features/apps/partials/edit.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/app/features/apps/partials/edit.html b/public/app/features/apps/partials/edit.html index aae34b8b38d..20df40bd247 100644 --- a/public/app/features/apps/partials/edit.html +++ b/public/app/features/apps/partials/edit.html @@ -98,6 +98,8 @@
+
+
From 1bca28ad12478dc2bbefb759bde00c337e0783f9 Mon Sep 17 00:00:00 2001 From: Anthony Woods Date: Thu, 21 Jan 2016 02:23:24 +0800 Subject: [PATCH 10/27] include the jsonData in the AppSettings DTO --- pkg/api/dtos/apps.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/api/dtos/apps.go b/pkg/api/dtos/apps.go index b2b33370135..d10883a26d7 100644 --- a/pkg/api/dtos/apps.go +++ b/pkg/api/dtos/apps.go @@ -31,6 +31,7 @@ func NewAppSettingsDto(def *plugins.AppPlugin, data *models.AppSettings) *AppSet dto.Enabled = data.Enabled dto.Pinned = data.Pinned dto.Info = &def.Info + dto.JsonData = data.JsonData } return dto From 958649363291f0a561d39d3bcd082f4103e786bd Mon Sep 17 00:00:00 2001 From: Anthony Woods Date: Thu, 21 Jan 2016 11:37:48 +0800 Subject: [PATCH 11/27] add apiPluginHeader support --- pkg/plugins/api_plugin.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pkg/plugins/api_plugin.go b/pkg/plugins/api_plugin.go index 0a47b7d18b2..0165370a222 100644 --- a/pkg/plugins/api_plugin.go +++ b/pkg/plugins/api_plugin.go @@ -7,12 +7,13 @@ import ( ) type ApiPluginRoute struct { - Path string `json:"path"` - Method string `json:"method"` - ReqSignedIn bool `json:"reqSignedIn"` - ReqGrafanaAdmin bool `json:"reqGrafanaAdmin"` - ReqRole models.RoleType `json:"reqRole"` - Url string `json:"url"` + Path string `json:"path"` + Method string `json:"method"` + ReqSignedIn bool `json:"reqSignedIn"` + ReqGrafanaAdmin bool `json:"reqGrafanaAdmin"` + ReqRole models.RoleType `json:"reqRole"` + Url string `json:"url"` + Headers []ApiPluginHeader `json:"headers"` } type ApiPlugin struct { @@ -20,6 +21,11 @@ type ApiPlugin struct { Routes []*ApiPluginRoute `json:"routes"` } +type ApiPluginHeader struct { + Name string `json:"name"` + Content string `json:"content"` +} + func (app *ApiPlugin) Load(decoder *json.Decoder, pluginDir string) error { if err := decoder.Decode(&app); err != nil { return err From 462608517be79d06d6cee3420efcfbda2f6eec85 Mon Sep 17 00:00:00 2001 From: Anthony Woods Date: Thu, 21 Jan 2016 11:38:27 +0800 Subject: [PATCH 12/27] set pluginDir of apiPlugin object --- pkg/plugins/api_plugin.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/plugins/api_plugin.go b/pkg/plugins/api_plugin.go index 0165370a222..efb16e696d8 100644 --- a/pkg/plugins/api_plugin.go +++ b/pkg/plugins/api_plugin.go @@ -31,6 +31,8 @@ func (app *ApiPlugin) Load(decoder *json.Decoder, pluginDir string) error { return err } + app.PluginDir = pluginDir + ApiPlugins[app.Id] = app return nil } From fd52320460fa4760ef26d2d798af0e9a687fbdcf Mon Sep 17 00:00:00 2001 From: Anthony Woods Date: Thu, 21 Jan 2016 11:39:51 +0800 Subject: [PATCH 13/27] set includedAppId of apiPlugins --- pkg/plugins/app_plugin.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/plugins/app_plugin.go b/pkg/plugins/app_plugin.go index 898065be8a2..d2291d487ac 100644 --- a/pkg/plugins/app_plugin.go +++ b/pkg/plugins/app_plugin.go @@ -59,6 +59,18 @@ func (app *AppPlugin) Load(decoder *json.Decoder, pluginDir string) error { } } + // check if we have child apiPlugins + for _, plugin := range ApiPlugins { + if strings.HasPrefix(plugin.PluginDir, app.PluginDir) { + plugin.IncludedInAppId = app.Id + app.Includes = append(app.Includes, AppIncludeInfo{ + Name: plugin.Name, + Id: plugin.Id, + Type: plugin.Type, + }) + } + } + Apps[app.Id] = app return nil } From 423eca6e7d99c4fc6c8bfdfa99181dd834d36820 Mon Sep 17 00:00:00 2001 From: Anthony Woods Date: Thu, 21 Jan 2016 11:41:02 +0800 Subject: [PATCH 14/27] add support for fetching appSettings by appId --- pkg/models/app_settings.go | 15 ++++++++++++++- pkg/services/sqlstore/app_settings.go | 13 +++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/pkg/models/app_settings.go b/pkg/models/app_settings.go index 558946d0277..f3b60502cb0 100644 --- a/pkg/models/app_settings.go +++ b/pkg/models/app_settings.go @@ -1,6 +1,13 @@ package models -import "time" +import ( + "errors" + "time" +) + +var ( + ErrAppSettingNotFound = errors.New("AppSetting not found") +) type AppSettings struct { Id int64 @@ -33,3 +40,9 @@ type GetAppSettingsQuery struct { OrgId int64 Result []*AppSettings } + +type GetAppSettingByAppIdQuery struct { + AppId string + OrgId int64 + Result *AppSettings +} diff --git a/pkg/services/sqlstore/app_settings.go b/pkg/services/sqlstore/app_settings.go index e9bfbeaa73b..7d9482e7b22 100644 --- a/pkg/services/sqlstore/app_settings.go +++ b/pkg/services/sqlstore/app_settings.go @@ -9,6 +9,7 @@ import ( func init() { bus.AddHandler("sql", GetAppSettings) + bus.AddHandler("sql", GetAppSettingByAppId) bus.AddHandler("sql", UpdateAppSettings) } @@ -19,6 +20,18 @@ func GetAppSettings(query *m.GetAppSettingsQuery) error { return sess.Find(&query.Result) } +func GetAppSettingByAppId(query *m.GetAppSettingByAppIdQuery) error { + appSetting := m.AppSettings{OrgId: query.OrgId, AppId: query.AppId} + has, err := x.Get(&appSetting) + if err != nil { + return err + } else if has == false { + return m.ErrAppSettingNotFound + } + query.Result = &appSetting + return nil +} + func UpdateAppSettings(cmd *m.UpdateAppSettingsCmd) error { return inTransaction2(func(sess *session) error { var app m.AppSettings From dcde1422b786faffed5b0f636068526c06f8761e Mon Sep 17 00:00:00 2001 From: Anthony Woods Date: Thu, 21 Jan 2016 11:41:59 +0800 Subject: [PATCH 15/27] add support for apiPlugin headers. The header content uses text/Template. If the apiPlugin is bundled in an App, then the appSettings.JsonData is passed to the header template. --- pkg/api/api_plugin.go | 63 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 12 deletions(-) diff --git a/pkg/api/api_plugin.go b/pkg/api/api_plugin.go index b14afec3a62..751c234bfe8 100644 --- a/pkg/api/api_plugin.go +++ b/pkg/api/api_plugin.go @@ -1,13 +1,17 @@ package api import ( + "bytes" "encoding/json" + "fmt" "net/http" "net/http/httputil" "net/url" + "text/template" "gopkg.in/macaron.v1" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" @@ -34,32 +38,28 @@ func InitApiPluginRoutes(r *macaron.Macaron) { handlers = append(handlers, middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN)) } } - handlers = append(handlers, ApiPlugin(route.Url)) + handlers = append(handlers, ApiPlugin(route, plugin.IncludedInAppId)) r.Route(url, route.Method, handlers...) log.Info("Plugin: Adding route %s", url) } } } -func ApiPlugin(routeUrl string) macaron.Handler { +func ApiPlugin(route *plugins.ApiPluginRoute, includedInAppId string) macaron.Handler { return func(c *middleware.Context) { path := c.Params("*") - //Create a HTTP header with the context in it. - ctx, err := json.Marshal(c.SignedInUser) - if err != nil { - c.JsonApiErr(500, "failed to marshal context to json.", err) - return - } - targetUrl, _ := url.Parse(routeUrl) - proxy := NewApiPluginProxy(string(ctx), path, targetUrl) + proxy := NewApiPluginProxy(c, path, route, includedInAppId) proxy.Transport = dataProxyTransport proxy.ServeHTTP(c.Resp, c.Req.Request) } } -func NewApiPluginProxy(ctx string, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy { +func NewApiPluginProxy(ctx *middleware.Context, proxyPath string, route *plugins.ApiPluginRoute, includedInAppId string) *httputil.ReverseProxy { + targetUrl, _ := url.Parse(route.Url) + director := func(req *http.Request) { + req.URL.Scheme = targetUrl.Scheme req.URL.Host = targetUrl.Host req.Host = targetUrl.Host @@ -69,7 +69,46 @@ func NewApiPluginProxy(ctx string, proxyPath string, targetUrl *url.URL) *httput // clear cookie headers req.Header.Del("Cookie") req.Header.Del("Set-Cookie") - req.Header.Add("Grafana-Context", ctx) + + //Create a HTTP header with the context in it. + ctxJson, err := json.Marshal(ctx.SignedInUser) + if err != nil { + ctx.JsonApiErr(500, "failed to marshal context to json.", err) + return + } + + req.Header.Add("Grafana-Context", string(ctxJson)) + // add custom headers defined in the plugin config. + for _, header := range route.Headers { + var contentBuf bytes.Buffer + t, err := template.New("content").Parse(header.Content) + if err != nil { + ctx.JsonApiErr(500, fmt.Sprintf("could not parse header content template for header %s.", header.Name), err) + return + } + + jsonData := make(map[string]interface{}) + + if includedInAppId != "" { + //lookup appSettings + query := m.GetAppSettingByAppIdQuery{OrgId: ctx.OrgId, AppId: includedInAppId} + + if err := bus.Dispatch(&query); err != nil { + ctx.JsonApiErr(500, "failed to get AppSettings of includedAppId.", err) + return + } + + jsonData = query.Result.JsonData + } + + err = t.Execute(&contentBuf, jsonData) + if err != nil { + ctx.JsonApiErr(500, fmt.Sprintf("failed to execute header content template for header %s.", header.Name), err) + return + } + log.Debug("Adding header to proxy request. %s: %s", header.Name, contentBuf.String()) + req.Header.Add(header.Name, contentBuf.String()) + } } return &httputil.ReverseProxy{Director: director} From cdcc7a71723094c3f858a521cad27b1378a15e46 Mon Sep 17 00:00:00 2001 From: matteo brancaleoni Date: Wed, 20 Jan 2016 16:44:31 +0100 Subject: [PATCH 16/27] add export to csv in table panel --- public/app/core/utils/kbn.js | 25 ++++++++++++++++++-- public/app/plugins/panel/table/controller.ts | 7 ++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/public/app/core/utils/kbn.js b/public/app/core/utils/kbn.js index e323a9223e3..e3a9fbaa39e 100644 --- a/public/app/core/utils/kbn.js +++ b/public/app/core/utils/kbn.js @@ -186,8 +186,29 @@ function($, _) { text += series.alias + ';' + new Date(dp[1]).toISOString() + ';' + dp[0] + '\n'; }); }); - var blob = new Blob([text], { type: "text/csv;charset=utf-8" }); - window.saveAs(blob, 'grafana_data_export.csv'); + kbn.saveSaveBlob(text, 'grafana_data_export.csv'); + }; + + kbn.exportTableDataToCsv = function(table) { + var text = ''; + // add header + _.each(table.columns, function(column) { + text += column.text + ';'; + }); + text += '\n'; + // process data + _.each(table.rows, function(row) { + _.each(row, function(value) { + text += value + ';'; + }); + text += '\n'; + }); + kbn.saveSaveBlob(text, 'grafana_data_export.csv'); + }; + + kbn.saveSaveBlob = function(payload, fname) { + var blob = new Blob([payload], { type: "text/csv;charset=utf-8" }); + window.saveAs(blob, fname); }; kbn.stringToJsRegex = function(str) { diff --git a/public/app/plugins/panel/table/controller.ts b/public/app/plugins/panel/table/controller.ts index f05a36c4cdf..971f37de849 100644 --- a/public/app/plugins/panel/table/controller.ts +++ b/public/app/plugins/panel/table/controller.ts @@ -3,6 +3,7 @@ import angular from 'angular'; import _ from 'lodash'; import moment from 'moment'; +import kbn from 'app/core/utils/kbn'; import PanelMeta from 'app/features/panel/panel_meta2'; import {transformDataToTable} from './transformers'; @@ -23,6 +24,8 @@ export class TablePanelCtrl { $scope.panelMeta.addEditorTab('Options', 'app/plugins/panel/table/options.html'); $scope.panelMeta.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html'); + $scope.panelMeta.addExtendedMenuItem('Export CSV', '', 'exportCsv()'); + var panelDefaults = { targets: [{}], transform: 'timeseries_to_columns', @@ -124,6 +127,10 @@ export class TablePanelCtrl { panelHelper.broadcastRender($scope, $scope.table, $scope.dataRaw); }; + $scope.exportCsv = function() { + kbn.exportTableDataToCsv($scope.table); + }; + $scope.init(); } } From 4a8f82ca9b88ab4c2a72bf14e5d31a2b7555744b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 21 Jan 2016 10:10:12 +0100 Subject: [PATCH 17/27] fix(image rendering): fixed issue with image rendering, fixes #3804 --- public/less/sidemenu.less | 1 + public/views/index.html | 1 + tasks/options/concat.js | 1 + vendor/phantomjs/render.js | 104 ++++++++++++++++++++----------------- 4 files changed, 59 insertions(+), 48 deletions(-) diff --git a/public/less/sidemenu.less b/public/less/sidemenu.less index 3bb1ae393a7..61882f0e4c3 100644 --- a/public/less/sidemenu.less +++ b/public/less/sidemenu.less @@ -13,6 +13,7 @@ min-height: 100%; z-index: 101; transform: translate3d(-100%, 0, 0); + visibility: hidden; a:focus { text-decoration: none; diff --git a/public/views/index.html b/public/views/index.html index 2d05f414658..1d612e53385 100644 --- a/public/views/index.html +++ b/public/views/index.html @@ -56,6 +56,7 @@ + diff --git a/tasks/options/concat.js b/tasks/options/concat.js index f62e1f09982..7c2e9ed17c2 100644 --- a/tasks/options/concat.js +++ b/tasks/options/concat.js @@ -28,6 +28,7 @@ module.exports = function(config) { js: { src: [ + '<%= genDir %>/vendor/npm/es6-shim/es5-shim.js', '<%= genDir %>/vendor/npm/es6-shim/es6-shim.js', '<%= genDir %>/vendor/npm/es6-promise/es6-promise.js', '<%= genDir %>/vendor/npm/systemjs/dist/system.js', diff --git a/vendor/phantomjs/render.js b/vendor/phantomjs/render.js index 057d464a74c..95885a17886 100644 --- a/vendor/phantomjs/render.js +++ b/vendor/phantomjs/render.js @@ -1,55 +1,63 @@ -var page = require('webpage').create(); -var args = require('system').args; -var params = {}; -var regexp = /^([^=]+)=([^$]+)/; +(function() { + 'use strict'; -args.forEach(function(arg) { - var parts = arg.match(regexp); - if (!parts) { return; } - params[parts[1]] = parts[2]; -}); + var page = require('webpage').create(); + var args = require('system').args; + var params = {}; + var regexp = /^([^=]+)=([^$]+)/; -var usage = "url= png= width= height= cookiename= sessionid= domain="; + args.forEach(function(arg) { + var parts = arg.match(regexp); + if (!parts) { return; } + params[parts[1]] = parts[2]; + }); -if (!params.url || !params.png || !params.cookiename || ! params.sessionid || !params.domain) { - console.log(usage); - phantom.exit(); -} + var usage = "url= png= width= height= cookiename= sessionid= domain="; -phantom.addCookie({ - 'name': params.cookiename, - 'value': params.sessionid, - 'domain': params.domain -}); - -page.viewportSize = { - width: params.width || '800', - height: params.height || '400' -}; - -var tries = 0; - -page.open(params.url, function (status) { - console.log('Loading a web page: ' + params.url); - - function checkIsReady() { - var canvas = page.evaluate(function() { - var body = angular.element(document.body); // 1 - var rootScope = body.scope().$root; - var panelsToLoad = angular.element('div.panel').length; - return rootScope.performance.panelsRendered >= panelsToLoad; - }); - - if (canvas || tries === 1000) { - page.render(params.png); - phantom.exit(); - } - else { - tries++; - setTimeout(checkIsReady, 10); - } + if (!params.url || !params.png || !params.cookiename || ! params.sessionid || !params.domain) { + console.log(usage); + phantom.exit(); } - setTimeout(checkIsReady, 200); + phantom.addCookie({ + 'name': params.cookiename, + 'value': params.sessionid, + 'domain': params.domain + }); -}); + page.viewportSize = { + width: params.width || '800', + height: params.height || '400' + }; + + var tries = 0; + + page.open(params.url, function (status) { + console.log('Loading a web page: ' + params.url + ' status: ' + status); + + function checkIsReady() { + var canvas = page.evaluate(function() { + if (!window.angular) { return false; } + var body = window.angular.element(document.body); // 1 + if (!body.scope) { return false; } + + var rootScope = body.scope(); + if (!rootScope) {return false;} + if (!rootScope.performance) { return false; } + var panelsToLoad = window.angular.element('div.panel').length; + return rootScope.performance.panelsRendered >= panelsToLoad; + }); + + if (canvas || tries === 1000) { + page.render(params.png); + phantom.exit(); + } + else { + tries++; + setTimeout(checkIsReady, 10); + } + } + + setTimeout(checkIsReady, 200); + }); +})(); From 6461981aa47365090de7daa16fb0f1311fab6247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 20 Jan 2016 13:28:10 +0100 Subject: [PATCH 18/27] build(): minor change --- tasks/build_task.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/build_task.js b/tasks/build_task.js index d10a83d78dc..d844b661722 100644 --- a/tasks/build_task.js +++ b/tasks/build_task.js @@ -10,7 +10,7 @@ module.exports = function(grunt) { 'clean:release', 'copy:public_to_gen', 'typescript:build', - // 'karma:test', + 'karma:test', 'phantomjs', 'css', 'htmlmin:build', From cbb5811d8b6516aa888429205e980509f106e321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 21 Jan 2016 11:39:44 +0100 Subject: [PATCH 19/27] fix(cleanup): removed unused js file --- .../datasource/elasticsearch/directives.js | 56 ------------------- 1 file changed, 56 deletions(-) delete mode 100644 public/app/plugins/datasource/elasticsearch/directives.js diff --git a/public/app/plugins/datasource/elasticsearch/directives.js b/public/app/plugins/datasource/elasticsearch/directives.js deleted file mode 100644 index 3bb27885ef2..00000000000 --- a/public/app/plugins/datasource/elasticsearch/directives.js +++ /dev/null @@ -1,56 +0,0 @@ -define([ - 'angular', - './bucket_agg', - './metric_agg', -], -function (angular) { - 'use strict'; - - var module = angular.module('grafana.directives'); - - module.directive('metricQueryEditorElasticsearch', function() { - return {controller: 'ElasticQueryCtrl', templateUrl: 'app/plugins/datasource/elasticsearch/partials/query.editor.html'}; - }); - - module.directive('metricQueryOptionsElasticsearch', function() { - return {templateUrl: 'app/plugins/datasource/elasticsearch/partials/query.options.html'}; - }); - - module.directive('annotationsQueryEditorElasticsearch', function() { - return {templateUrl: 'app/plugins/datasource/elasticsearch/partials/annotations.editor.html'}; - }); - - module.directive('elastic', function() { - return {templateUrl: 'app/plugins/datasource/elasticsearch/partials/config.html'}; - }); - - module.directive('elasticMetricAgg', function() { - return { - templateUrl: 'app/plugins/datasource/elasticsearch/partials/metric_agg.html', - controller: 'ElasticMetricAggCtrl', - restrict: 'E', - scope: { - target: "=", - index: "=", - onChange: "&", - getFields: "&", - esVersion: '=' - } - }; - }); - - module.directive('elasticBucketAgg', function() { - return { - templateUrl: 'app/plugins/datasource/elasticsearch/partials/bucket_agg.html', - controller: 'ElasticBucketAggCtrl', - restrict: 'E', - scope: { - target: "=", - index: "=", - onChange: "&", - getFields: "&", - } - }; - }); - -}); From bc05cc49772c0962476145243aae94221ca52224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 21 Jan 2016 12:56:47 +0100 Subject: [PATCH 20/27] fix(phantomjs): another fix for phantomjs rendering, #3804 --- public/views/index.html | 1 + tasks/options/concat.js | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/public/views/index.html b/public/views/index.html index 1d612e53385..728cd084c88 100644 --- a/public/views/index.html +++ b/public/views/index.html @@ -59,6 +59,7 @@ + diff --git a/tasks/options/concat.js b/tasks/options/concat.js index 7c2e9ed17c2..0c76ce98d23 100644 --- a/tasks/options/concat.js +++ b/tasks/options/concat.js @@ -28,9 +28,10 @@ module.exports = function(config) { js: { src: [ - '<%= genDir %>/vendor/npm/es6-shim/es5-shim.js', + '<%= genDir %>/vendor/npm/es5-shim/es5-shim.js', '<%= genDir %>/vendor/npm/es6-shim/es6-shim.js', - '<%= genDir %>/vendor/npm/es6-promise/es6-promise.js', + '<%= genDir %>/vendor/npm/es6-promise/dist/es6-promise.js', + '<%= genDir %>/vendor/npm/systemjs/dist/system-polyfills.js', '<%= genDir %>/vendor/npm/systemjs/dist/system.js', '<%= genDir %>/app/system.conf.js', '<%= genDir %>/app/boot.js', From 3d353c7d6d266dee47b858499a5604b0c4c91efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 21 Jan 2016 13:14:25 +0100 Subject: [PATCH 21/27] fix(mixed datasource): fixed issue with mixed data source in optimized build --- public/app/plugins/datasource/mixed/datasource.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/plugins/datasource/mixed/datasource.ts b/public/app/plugins/datasource/mixed/datasource.ts index bd0628fe210..b5cea9cc5a8 100644 --- a/public/app/plugins/datasource/mixed/datasource.ts +++ b/public/app/plugins/datasource/mixed/datasource.ts @@ -5,6 +5,7 @@ import _ from 'lodash'; class MixedDatasource { + /** @ngInject */ constructor(private $q, private datasourceSrv) { } From ba65b89bbb62ff85a6e6327936ca07f55efeb52a Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 21 Jan 2016 14:21:38 +0100 Subject: [PATCH 22/27] feat(csv export): extract csv export into a new file --- public/app/core/utils/file_export.ts | 74 ++++++++++++++++++++ public/app/core/utils/kbn.js | 32 --------- public/app/plugins/panel/graph/module.js | 5 +- public/app/plugins/panel/table/controller.ts | 4 +- 4 files changed, 79 insertions(+), 36 deletions(-) create mode 100644 public/app/core/utils/file_export.ts diff --git a/public/app/core/utils/file_export.ts b/public/app/core/utils/file_export.ts new file mode 100644 index 00000000000..c3959aec12f --- /dev/null +++ b/public/app/core/utils/file_export.ts @@ -0,0 +1,74 @@ +/// + +import _ from 'lodash'; + +declare var window: any; + +export function exportSeriesListToCsv(seriesList) { + var text = 'Series;Time;Value\n'; + _.each(seriesList, function(series) { + _.each(series.datapoints, function(dp) { + text += series.alias + ';' + new Date(dp[1]).toISOString() + ';' + dp[0] + '\n'; + }); + }); + saveSaveBlob(text, 'grafana_data_export.csv'); +}; + +export function exportTableDataToCsv(table) { + var text = ''; + // add header + _.each(table.columns, function(column) { + text += column.text + ';'; + }); + text += '\n'; + // process data + _.each(table.rows, function(row) { + _.each(row, function(value) { + text += value + ';'; + }); + text += '\n'; + }); + saveSaveBlob(text, 'grafana_data_export.csv'); +}; + +export function saveSaveBlob(payload, fname) { + var blob = new Blob([payload], { type: "text/csv;charset=utf-8" }); + window.saveAs(blob, fname); +}; + +/* +export default function flatten(target, opts): any { + opts = opts || {}; + + var delimiter = opts.delimiter || '.'; + var maxDepth = opts.maxDepth || 3; + var currentDepth = 1; + var output = {}; + + function step(object, prev) { + Object.keys(object).forEach(function(key) { + var value = object[key]; + var isarray = opts.safe && Array.isArray(value); + var type = Object.prototype.toString.call(value); + var isobject = type === "[object Object]"; + + var newKey = prev ? prev + delimiter + key : key; + + if (!opts.maxDepth) { + maxDepth = currentDepth + 1; + } + + if (!isarray && isobject && Object.keys(value).length && currentDepth < maxDepth) { + ++currentDepth; + return step(value, newKey); + } + + output[newKey] = value; + }); + } + + step(target, null); + + return output; +} +*/ \ No newline at end of file diff --git a/public/app/core/utils/kbn.js b/public/app/core/utils/kbn.js index e3a9fbaa39e..959de5fa42a 100644 --- a/public/app/core/utils/kbn.js +++ b/public/app/core/utils/kbn.js @@ -179,38 +179,6 @@ function($, _) { .replace(/ +/g,'-'); }; - kbn.exportSeriesListToCsv = function(seriesList) { - var text = 'Series;Time;Value\n'; - _.each(seriesList, function(series) { - _.each(series.datapoints, function(dp) { - text += series.alias + ';' + new Date(dp[1]).toISOString() + ';' + dp[0] + '\n'; - }); - }); - kbn.saveSaveBlob(text, 'grafana_data_export.csv'); - }; - - kbn.exportTableDataToCsv = function(table) { - var text = ''; - // add header - _.each(table.columns, function(column) { - text += column.text + ';'; - }); - text += '\n'; - // process data - _.each(table.rows, function(row) { - _.each(row, function(value) { - text += value + ';'; - }); - text += '\n'; - }); - kbn.saveSaveBlob(text, 'grafana_data_export.csv'); - }; - - kbn.saveSaveBlob = function(payload, fname) { - var blob = new Blob([payload], { type: "text/csv;charset=utf-8" }); - window.saveAs(blob, fname); - }; - kbn.stringToJsRegex = function(str) { if (str[0] !== '/') { return new RegExp('^' + str + '$'); diff --git a/public/app/plugins/panel/graph/module.js b/public/app/plugins/panel/graph/module.js index 47d0e4f83a3..c1039e394a3 100644 --- a/public/app/plugins/panel/graph/module.js +++ b/public/app/plugins/panel/graph/module.js @@ -3,13 +3,14 @@ define([ 'lodash', 'moment', 'app/core/utils/kbn', + 'app/core/utils/file_export', 'app/core/time_series', 'app/features/panel/panel_meta', './seriesOverridesCtrl', './graph', './legend', ], -function (angular, _, moment, kbn, TimeSeries, PanelMeta) { +function (angular, _, moment, kbn, fileExport, TimeSeries, PanelMeta) { 'use strict'; /** @ngInject */ @@ -282,7 +283,7 @@ function (angular, _, moment, kbn, TimeSeries, PanelMeta) { }; $scope.exportCsv = function() { - kbn.exportSeriesListToCsv($scope.seriesList); + fileExport.exportSeriesListToCsv($scope.seriesList); }; panelSrv.init($scope); diff --git a/public/app/plugins/panel/table/controller.ts b/public/app/plugins/panel/table/controller.ts index 971f37de849..97c8f7a728a 100644 --- a/public/app/plugins/panel/table/controller.ts +++ b/public/app/plugins/panel/table/controller.ts @@ -3,7 +3,7 @@ import angular from 'angular'; import _ from 'lodash'; import moment from 'moment'; -import kbn from 'app/core/utils/kbn'; +import * as FileExport from 'app/core/utils/file_export'; import PanelMeta from 'app/features/panel/panel_meta2'; import {transformDataToTable} from './transformers'; @@ -128,7 +128,7 @@ export class TablePanelCtrl { }; $scope.exportCsv = function() { - kbn.exportTableDataToCsv($scope.table); + FileExport.exportTableDataToCsv($scope.table); }; $scope.init(); From d3f90dcfcc0ce354858fd2c754cf7271b1ec461e Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 21 Jan 2016 14:26:31 +0100 Subject: [PATCH 23/27] style(fileexport): remove commented code --- public/app/core/utils/file_export.ts | 39 +--------------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/public/app/core/utils/file_export.ts b/public/app/core/utils/file_export.ts index c3959aec12f..ec8a604c1c2 100644 --- a/public/app/core/utils/file_export.ts +++ b/public/app/core/utils/file_export.ts @@ -34,41 +34,4 @@ export function exportTableDataToCsv(table) { export function saveSaveBlob(payload, fname) { var blob = new Blob([payload], { type: "text/csv;charset=utf-8" }); window.saveAs(blob, fname); -}; - -/* -export default function flatten(target, opts): any { - opts = opts || {}; - - var delimiter = opts.delimiter || '.'; - var maxDepth = opts.maxDepth || 3; - var currentDepth = 1; - var output = {}; - - function step(object, prev) { - Object.keys(object).forEach(function(key) { - var value = object[key]; - var isarray = opts.safe && Array.isArray(value); - var type = Object.prototype.toString.call(value); - var isobject = type === "[object Object]"; - - var newKey = prev ? prev + delimiter + key : key; - - if (!opts.maxDepth) { - maxDepth = currentDepth + 1; - } - - if (!isarray && isobject && Object.keys(value).length && currentDepth < maxDepth) { - ++currentDepth; - return step(value, newKey); - } - - output[newKey] = value; - }); - } - - step(target, null); - - return output; -} -*/ \ No newline at end of file +}; \ No newline at end of file From 08f02397b6bcdc584c47a3c86710d82b87d6efcb Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 21 Jan 2016 14:31:20 +0100 Subject: [PATCH 24/27] fix(fileexport): tslint fix --- public/app/core/utils/file_export.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/core/utils/file_export.ts b/public/app/core/utils/file_export.ts index ec8a604c1c2..ad203f58495 100644 --- a/public/app/core/utils/file_export.ts +++ b/public/app/core/utils/file_export.ts @@ -34,4 +34,4 @@ export function exportTableDataToCsv(table) { export function saveSaveBlob(payload, fname) { var blob = new Blob([payload], { type: "text/csv;charset=utf-8" }); window.saveAs(blob, fname); -}; \ No newline at end of file +}; From dfd4fbc566446df11c4ae85c3dd5913193690303 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 21 Jan 2016 14:39:32 +0100 Subject: [PATCH 25/27] feat(hooks): improve symlinks script --- symlink_git_hooks.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/symlink_git_hooks.sh b/symlink_git_hooks.sh index c8a90432a70..9e272f82ed6 100755 --- a/symlink_git_hooks.sh +++ b/symlink_git_hooks.sh @@ -1,3 +1,5 @@ #/bin/bash -ln -s -f .hooks/* .git/hooks/ +#ln -s -f .hooks/* .git/hooks/ +cd .git/hooks/ +cp --symbolic-link -f ../../.hooks/* . From e5e8e2021ff8253a300b146f11a07c3b3f73b701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20Fr=C3=B6hlich?= Date: Thu, 21 Jan 2016 14:58:15 +0100 Subject: [PATCH 26/27] Fix dashboard sorting in playlists --- public/app/features/playlist/playlist_edit_ctrl.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/features/playlist/playlist_edit_ctrl.js b/public/app/features/playlist/playlist_edit_ctrl.js index 3817ec12634..9b493c47d25 100644 --- a/public/app/features/playlist/playlist_edit_ctrl.js +++ b/public/app/features/playlist/playlist_edit_ctrl.js @@ -132,11 +132,11 @@ function (angular, config, _) { }; $scope.movePlaylistItemUp = function(playlistItem) { - $scope.moveDashboard(playlistItem, -1); + $scope.movePlaylistItem(playlistItem, -1); }; $scope.movePlaylistItemDown = function(playlistItem) { - $scope.moveDashboard(playlistItem, 1); + $scope.movePlaylistItem(playlistItem, 1); }; $scope.init(); From 28fabadeaebf8f3b152a654f4c44381faf2f828a Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 21 Jan 2016 15:04:23 +0100 Subject: [PATCH 27/27] style: remove empty row --- public/app/plugins/panel/table/controller.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/public/app/plugins/panel/table/controller.ts b/public/app/plugins/panel/table/controller.ts index 97c8f7a728a..376f36fd84d 100644 --- a/public/app/plugins/panel/table/controller.ts +++ b/public/app/plugins/panel/table/controller.ts @@ -23,7 +23,6 @@ export class TablePanelCtrl { $scope.panelMeta.addEditorTab('Options', 'app/plugins/panel/table/options.html'); $scope.panelMeta.addEditorTab('Time range', 'app/features/panel/partials/panelTime.html'); - $scope.panelMeta.addExtendedMenuItem('Export CSV', '', 'exportCsv()'); var panelDefaults = {