From 6e7941d39603aabc12df50e7f74dd27f1f95e393 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 7 Feb 2019 13:13:01 +0100 Subject: [PATCH 01/14] moves usage stats sender to new package --- pkg/infra/usagestats/service.go | 54 ++++++ pkg/infra/usagestats/usage_stats.go | 168 ++++++++++++++++ .../usagestats/usage_stats_test.go} | 21 +- pkg/metrics/metrics.go | 181 +----------------- pkg/metrics/service.go | 22 +-- pkg/metrics/settings.go | 4 - 6 files changed, 247 insertions(+), 203 deletions(-) create mode 100644 pkg/infra/usagestats/service.go create mode 100644 pkg/infra/usagestats/usage_stats.go rename pkg/{metrics/metrics_test.go => infra/usagestats/usage_stats_test.go} (94%) diff --git a/pkg/infra/usagestats/service.go b/pkg/infra/usagestats/service.go new file mode 100644 index 00000000000..f853c03302d --- /dev/null +++ b/pkg/infra/usagestats/service.go @@ -0,0 +1,54 @@ +package usagestats + +import ( + "context" + "time" + + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/services/auth" + "github.com/grafana/grafana/pkg/social" + + "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/registry" + "github.com/grafana/grafana/pkg/setting" +) + +var metricsLogger log.Logger = log.New("metrics") + +func init() { + registry.RegisterService(&UsageStatsService{}) +} + +type UsageStatsService struct { + Cfg *setting.Cfg `inject:""` + TokenService *auth.UserAuthTokenService `inject:""` + Bus bus.Bus `inject:""` + + oauthProviders map[string]bool +} + +func (uss *UsageStatsService) Init() error { + + uss.oauthProviders = social.GetOAuthProviders(uss.Cfg) + return nil +} + +func (uss *UsageStatsService) Run(ctx context.Context) error { + uss.updateTotalStats() + + onceEveryDayTick := time.NewTicker(time.Hour * 24) + everyMinuteTicker := time.NewTicker(time.Minute) + defer onceEveryDayTick.Stop() + defer everyMinuteTicker.Stop() + + for { + select { + case <-onceEveryDayTick.C: + uss.sendUsageStats(uss.oauthProviders) + case <-everyMinuteTicker.C: + uss.updateTotalStats() + case <-ctx.Done(): + return ctx.Err() + } + } +} diff --git a/pkg/infra/usagestats/usage_stats.go b/pkg/infra/usagestats/usage_stats.go new file mode 100644 index 00000000000..b0dc52ccd8b --- /dev/null +++ b/pkg/infra/usagestats/usage_stats.go @@ -0,0 +1,168 @@ +package usagestats + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "runtime" + "strings" + "time" + + "github.com/grafana/grafana/pkg/metrics" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/setting" +) + +var usageStatsURL = "https://stats.grafana.org/grafana-usage-report" + +func (uss *UsageStatsService) sendUsageStats(oauthProviders map[string]bool) { + if !setting.ReportingEnabled { + return + } + + metricsLogger.Debug(fmt.Sprintf("Sending anonymous usage stats to %s", usageStatsURL)) + + version := strings.Replace(setting.BuildVersion, ".", "_", -1) + + metrics := map[string]interface{}{} + report := map[string]interface{}{ + "version": version, + "metrics": metrics, + "os": runtime.GOOS, + "arch": runtime.GOARCH, + "edition": getEdition(), + "packaging": setting.Packaging, + } + + statsQuery := models.GetSystemStatsQuery{} + if err := uss.Bus.Dispatch(&statsQuery); err != nil { + metricsLogger.Error("Failed to get system stats", "error", err) + return + } + + metrics["stats.dashboards.count"] = statsQuery.Result.Dashboards + metrics["stats.users.count"] = statsQuery.Result.Users + metrics["stats.orgs.count"] = statsQuery.Result.Orgs + metrics["stats.playlist.count"] = statsQuery.Result.Playlists + metrics["stats.plugins.apps.count"] = len(plugins.Apps) + metrics["stats.plugins.panels.count"] = len(plugins.Panels) + metrics["stats.plugins.datasources.count"] = len(plugins.DataSources) + metrics["stats.alerts.count"] = statsQuery.Result.Alerts + metrics["stats.active_users.count"] = statsQuery.Result.ActiveUsers + metrics["stats.datasources.count"] = statsQuery.Result.Datasources + metrics["stats.stars.count"] = statsQuery.Result.Stars + metrics["stats.folders.count"] = statsQuery.Result.Folders + metrics["stats.dashboard_permissions.count"] = statsQuery.Result.DashboardPermissions + metrics["stats.folder_permissions.count"] = statsQuery.Result.FolderPermissions + metrics["stats.provisioned_dashboards.count"] = statsQuery.Result.ProvisionedDashboards + metrics["stats.snapshots.count"] = statsQuery.Result.Snapshots + metrics["stats.teams.count"] = statsQuery.Result.Teams + + dsStats := models.GetDataSourceStatsQuery{} + if err := uss.Bus.Dispatch(&dsStats); err != nil { + metricsLogger.Error("Failed to get datasource stats", "error", err) + return + } + + // send counters for each data source + // but ignore any custom data sources + // as sending that name could be sensitive information + dsOtherCount := 0 + for _, dsStat := range dsStats.Result { + if models.IsKnownDataSourcePlugin(dsStat.Type) { + metrics["stats.ds."+dsStat.Type+".count"] = dsStat.Count + } else { + dsOtherCount += dsStat.Count + } + } + metrics["stats.ds.other.count"] = dsOtherCount + + metrics["stats.packaging."+setting.Packaging+".count"] = 1 + + dsAccessStats := models.GetDataSourceAccessStatsQuery{} + if err := uss.Bus.Dispatch(&dsAccessStats); err != nil { + metricsLogger.Error("Failed to get datasource access stats", "error", err) + return + } + + // send access counters for each data source + // but ignore any custom data sources + // as sending that name could be sensitive information + dsAccessOtherCount := make(map[string]int64) + for _, dsAccessStat := range dsAccessStats.Result { + if dsAccessStat.Access == "" { + continue + } + + access := strings.ToLower(dsAccessStat.Access) + + if models.IsKnownDataSourcePlugin(dsAccessStat.Type) { + metrics["stats.ds_access."+dsAccessStat.Type+"."+access+".count"] = dsAccessStat.Count + } else { + old := dsAccessOtherCount[access] + dsAccessOtherCount[access] = old + dsAccessStat.Count + } + } + + for access, count := range dsAccessOtherCount { + metrics["stats.ds_access.other."+access+".count"] = count + } + + anStats := models.GetAlertNotifierUsageStatsQuery{} + if err := uss.Bus.Dispatch(&anStats); err != nil { + metricsLogger.Error("Failed to get alert notification stats", "error", err) + return + } + + for _, stats := range anStats.Result { + metrics["stats.alert_notifiers."+stats.Type+".count"] = stats.Count + } + + authTypes := map[string]bool{} + authTypes["anonymous"] = setting.AnonymousEnabled + authTypes["basic_auth"] = setting.BasicAuthEnabled + authTypes["ldap"] = setting.LdapEnabled + authTypes["auth_proxy"] = setting.AuthProxyEnabled + + for provider, enabled := range oauthProviders { + authTypes["oauth_"+provider] = enabled + } + + for authType, enabled := range authTypes { + enabledValue := 0 + if enabled { + enabledValue = 1 + } + metrics["stats.auth_enabled."+authType+".count"] = enabledValue + } + + out, _ := json.MarshalIndent(report, "", " ") + data := bytes.NewBuffer(out) + + client := http.Client{Timeout: 5 * time.Second} + go client.Post(usageStatsURL, "application/json", data) +} + +func (uss *UsageStatsService) updateTotalStats() { + statsQuery := models.GetSystemStatsQuery{} + if err := uss.Bus.Dispatch(&statsQuery); err != nil { + metricsLogger.Error("Failed to get system stats", "error", err) + return + } + + metrics.M_StatTotal_Dashboards.Set(float64(statsQuery.Result.Dashboards)) + metrics.M_StatTotal_Users.Set(float64(statsQuery.Result.Users)) + metrics.M_StatActive_Users.Set(float64(statsQuery.Result.ActiveUsers)) + metrics.M_StatTotal_Playlists.Set(float64(statsQuery.Result.Playlists)) + metrics.M_StatTotal_Orgs.Set(float64(statsQuery.Result.Orgs)) +} + +func getEdition() string { + if setting.IsEnterprise { + return "enterprise" + } else { + return "oss" + } +} diff --git a/pkg/metrics/metrics_test.go b/pkg/infra/usagestats/usage_stats_test.go similarity index 94% rename from pkg/metrics/metrics_test.go rename to pkg/infra/usagestats/usage_stats_test.go index c27d6f64b8c..dd45e96f256 100644 --- a/pkg/metrics/metrics_test.go +++ b/pkg/infra/usagestats/usage_stats_test.go @@ -1,4 +1,4 @@ -package metrics +package usagestats import ( "bytes" @@ -21,8 +21,13 @@ import ( func TestMetrics(t *testing.T) { Convey("Test send usage stats", t, func() { + uss := &UsageStatsService{ + Bus: bus.New(), + } + var getSystemStatsQuery *models.GetSystemStatsQuery - bus.AddHandler("test", func(query *models.GetSystemStatsQuery) error { + uss.Bus.AddHandler(func(query *models.GetSystemStatsQuery) error { + query.Result = &models.SystemStats{ Dashboards: 1, Datasources: 2, @@ -44,7 +49,7 @@ func TestMetrics(t *testing.T) { }) var getDataSourceStatsQuery *models.GetDataSourceStatsQuery - bus.AddHandler("test", func(query *models.GetDataSourceStatsQuery) error { + uss.Bus.AddHandler(func(query *models.GetDataSourceStatsQuery) error { query.Result = []*models.DataSourceStats{ { Type: models.DS_ES, @@ -68,7 +73,7 @@ func TestMetrics(t *testing.T) { }) var getDataSourceAccessStatsQuery *models.GetDataSourceAccessStatsQuery - bus.AddHandler("test", func(query *models.GetDataSourceAccessStatsQuery) error { + uss.Bus.AddHandler(func(query *models.GetDataSourceAccessStatsQuery) error { query.Result = []*models.DataSourceAccessStats{ { Type: models.DS_ES, @@ -116,7 +121,7 @@ func TestMetrics(t *testing.T) { }) var getAlertNotifierUsageStatsQuery *models.GetAlertNotifierUsageStatsQuery - bus.AddHandler("test", func(query *models.GetAlertNotifierUsageStatsQuery) error { + uss.Bus.AddHandler(func(query *models.GetAlertNotifierUsageStatsQuery) error { query.Result = []*models.NotifierUsageStats{ { Type: "slack", @@ -155,11 +160,11 @@ func TestMetrics(t *testing.T) { "grafana_com": true, } - sendUsageStats(oauthProviders) + uss.sendUsageStats(oauthProviders) Convey("Given reporting not enabled and sending usage stats", func() { setting.ReportingEnabled = false - sendUsageStats(oauthProviders) + uss.sendUsageStats(oauthProviders) Convey("Should not gather stats or call http endpoint", func() { So(getSystemStatsQuery, ShouldBeNil) @@ -179,7 +184,7 @@ func TestMetrics(t *testing.T) { setting.Packaging = "deb" wg.Add(1) - sendUsageStats(oauthProviders) + uss.sendUsageStats(oauthProviders) Convey("Should gather stats and call http endpoint", func() { if waitTimeout(&wg, 2*time.Second) { diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 326514a9687..718a63ee768 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -1,17 +1,8 @@ package metrics import ( - "bytes" - "encoding/json" - "net/http" "runtime" - "strings" - "time" - "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/models" - "github.com/grafana/grafana/pkg/plugins" - "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/client_golang/prometheus" ) @@ -68,23 +59,6 @@ var ( grafanaBuildVersion *prometheus.GaugeVec ) -func newCounterVecStartingAtZero(opts prometheus.CounterOpts, labels []string, labelValues ...string) *prometheus.CounterVec { - counter := prometheus.NewCounterVec(opts, labels) - - for _, label := range labelValues { - counter.WithLabelValues(label).Add(0) - } - - return counter -} - -func newCounterStartingAtZero(opts prometheus.CounterOpts, labelValues ...string) prometheus.Counter { - counter := prometheus.NewCounter(opts) - counter.Add(0) - - return counter -} - func init() { M_Instance_Start = prometheus.NewCounter(prometheus.CounterOpts{ Name: "instance_start_total", @@ -362,154 +336,19 @@ func initMetricVars() { } -func updateTotalStats() { - statsQuery := models.GetSystemStatsQuery{} - if err := bus.Dispatch(&statsQuery); err != nil { - metricsLogger.Error("Failed to get system stats", "error", err) - return +func newCounterVecStartingAtZero(opts prometheus.CounterOpts, labels []string, labelValues ...string) *prometheus.CounterVec { + counter := prometheus.NewCounterVec(opts, labels) + + for _, label := range labelValues { + counter.WithLabelValues(label).Add(0) } - M_StatTotal_Dashboards.Set(float64(statsQuery.Result.Dashboards)) - M_StatTotal_Users.Set(float64(statsQuery.Result.Users)) - M_StatActive_Users.Set(float64(statsQuery.Result.ActiveUsers)) - M_StatTotal_Playlists.Set(float64(statsQuery.Result.Playlists)) - M_StatTotal_Orgs.Set(float64(statsQuery.Result.Orgs)) + return counter } -var usageStatsURL = "https://stats.grafana.org/grafana-usage-report" +func newCounterStartingAtZero(opts prometheus.CounterOpts, labelValues ...string) prometheus.Counter { + counter := prometheus.NewCounter(opts) + counter.Add(0) -func getEdition() string { - if setting.IsEnterprise { - return "enterprise" - } else { - return "oss" - } -} - -func sendUsageStats(oauthProviders map[string]bool) { - if !setting.ReportingEnabled { - return - } - - metricsLogger.Debug("Sending anonymous usage stats to stats.grafana.org") - - version := strings.Replace(setting.BuildVersion, ".", "_", -1) - - metrics := map[string]interface{}{} - report := map[string]interface{}{ - "version": version, - "metrics": metrics, - "os": runtime.GOOS, - "arch": runtime.GOARCH, - "edition": getEdition(), - "packaging": setting.Packaging, - } - - statsQuery := models.GetSystemStatsQuery{} - if err := bus.Dispatch(&statsQuery); err != nil { - metricsLogger.Error("Failed to get system stats", "error", err) - return - } - - metrics["stats.dashboards.count"] = statsQuery.Result.Dashboards - metrics["stats.users.count"] = statsQuery.Result.Users - metrics["stats.orgs.count"] = statsQuery.Result.Orgs - metrics["stats.playlist.count"] = statsQuery.Result.Playlists - metrics["stats.plugins.apps.count"] = len(plugins.Apps) - metrics["stats.plugins.panels.count"] = len(plugins.Panels) - metrics["stats.plugins.datasources.count"] = len(plugins.DataSources) - metrics["stats.alerts.count"] = statsQuery.Result.Alerts - metrics["stats.active_users.count"] = statsQuery.Result.ActiveUsers - metrics["stats.datasources.count"] = statsQuery.Result.Datasources - metrics["stats.stars.count"] = statsQuery.Result.Stars - metrics["stats.folders.count"] = statsQuery.Result.Folders - metrics["stats.dashboard_permissions.count"] = statsQuery.Result.DashboardPermissions - metrics["stats.folder_permissions.count"] = statsQuery.Result.FolderPermissions - metrics["stats.provisioned_dashboards.count"] = statsQuery.Result.ProvisionedDashboards - metrics["stats.snapshots.count"] = statsQuery.Result.Snapshots - metrics["stats.teams.count"] = statsQuery.Result.Teams - - dsStats := models.GetDataSourceStatsQuery{} - if err := bus.Dispatch(&dsStats); err != nil { - metricsLogger.Error("Failed to get datasource stats", "error", err) - return - } - - // send counters for each data source - // but ignore any custom data sources - // as sending that name could be sensitive information - dsOtherCount := 0 - for _, dsStat := range dsStats.Result { - if models.IsKnownDataSourcePlugin(dsStat.Type) { - metrics["stats.ds."+dsStat.Type+".count"] = dsStat.Count - } else { - dsOtherCount += dsStat.Count - } - } - metrics["stats.ds.other.count"] = dsOtherCount - - metrics["stats.packaging."+setting.Packaging+".count"] = 1 - - dsAccessStats := models.GetDataSourceAccessStatsQuery{} - if err := bus.Dispatch(&dsAccessStats); err != nil { - metricsLogger.Error("Failed to get datasource access stats", "error", err) - return - } - - // send access counters for each data source - // but ignore any custom data sources - // as sending that name could be sensitive information - dsAccessOtherCount := make(map[string]int64) - for _, dsAccessStat := range dsAccessStats.Result { - if dsAccessStat.Access == "" { - continue - } - - access := strings.ToLower(dsAccessStat.Access) - - if models.IsKnownDataSourcePlugin(dsAccessStat.Type) { - metrics["stats.ds_access."+dsAccessStat.Type+"."+access+".count"] = dsAccessStat.Count - } else { - old := dsAccessOtherCount[access] - dsAccessOtherCount[access] = old + dsAccessStat.Count - } - } - - for access, count := range dsAccessOtherCount { - metrics["stats.ds_access.other."+access+".count"] = count - } - - anStats := models.GetAlertNotifierUsageStatsQuery{} - if err := bus.Dispatch(&anStats); err != nil { - metricsLogger.Error("Failed to get alert notification stats", "error", err) - return - } - - for _, stats := range anStats.Result { - metrics["stats.alert_notifiers."+stats.Type+".count"] = stats.Count - } - - authTypes := map[string]bool{} - authTypes["anonymous"] = setting.AnonymousEnabled - authTypes["basic_auth"] = setting.BasicAuthEnabled - authTypes["ldap"] = setting.LdapEnabled - authTypes["auth_proxy"] = setting.AuthProxyEnabled - - for provider, enabled := range oauthProviders { - authTypes["oauth_"+provider] = enabled - } - - for authType, enabled := range authTypes { - enabledValue := 0 - if enabled { - enabledValue = 1 - } - metrics["stats.auth_enabled."+authType+".count"] = enabledValue - } - - out, _ := json.MarshalIndent(report, "", " ") - data := bytes.NewBuffer(out) - - client := http.Client{Timeout: 5 * time.Second} - go client.Post(usageStatsURL, "application/json", data) + return counter } diff --git a/pkg/metrics/service.go b/pkg/metrics/service.go index d2c0c815da9..44b83187cac 100644 --- a/pkg/metrics/service.go +++ b/pkg/metrics/service.go @@ -2,7 +2,6 @@ package metrics import ( "context" - "time" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/metrics/graphitebridge" @@ -30,7 +29,6 @@ type InternalMetricsService struct { intervalSeconds int64 graphiteCfg *graphitebridge.Config - oauthProviders map[string]bool } func (im *InternalMetricsService) Init() error { @@ -50,22 +48,6 @@ func (im *InternalMetricsService) Run(ctx context.Context) error { M_Instance_Start.Inc() - // set the total stats gauges before we publishing metrics - updateTotalStats() - - onceEveryDayTick := time.NewTicker(time.Hour * 24) - everyMinuteTicker := time.NewTicker(time.Minute) - defer onceEveryDayTick.Stop() - defer everyMinuteTicker.Stop() - - for { - select { - case <-onceEveryDayTick.C: - sendUsageStats(im.oauthProviders) - case <-everyMinuteTicker.C: - updateTotalStats() - case <-ctx.Done(): - return ctx.Err() - } - } + <-ctx.Done() + return ctx.Err() } diff --git a/pkg/metrics/settings.go b/pkg/metrics/settings.go index 18b9e78d6ff..048e4134690 100644 --- a/pkg/metrics/settings.go +++ b/pkg/metrics/settings.go @@ -5,8 +5,6 @@ import ( "strings" "time" - "github.com/grafana/grafana/pkg/social" - "github.com/grafana/grafana/pkg/metrics/graphitebridge" "github.com/grafana/grafana/pkg/setting" "github.com/prometheus/client_golang/prometheus" @@ -24,8 +22,6 @@ func (im *InternalMetricsService) readSettings() error { return fmt.Errorf("Unable to parse metrics graphite section, %v", err) } - im.oauthProviders = social.GetOAuthProviders(im.Cfg) - return nil } From e0809831470ab08153763d982e9aa68eba2f441c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 7 Feb 2019 16:14:11 +0100 Subject: [PATCH 02/14] Panel edit navbar poc --- .../dashboard/components/DashNav/DashNav.tsx | 57 ++++++++++------ .../dashboard/containers/DashboardPage.tsx | 3 +- public/sass/components/_navbar.scss | 65 ++++++++++++++++++- public/sass/components/_panel_editor.scss | 4 ++ 4 files changed, 107 insertions(+), 22 deletions(-) diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index 297d7ca7ea7..8560b3bfbba 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -14,10 +14,11 @@ import { DashNavButton } from './DashNavButton'; import { updateLocation } from 'app/core/actions'; // Types -import { DashboardModel } from '../../state/DashboardModel'; +import { DashboardModel, PanelModel } from '../../state'; export interface Props { dashboard: DashboardModel; + fullscreenPanel?: PanelModel; editview: string; isEditing: boolean; isFullscreen: boolean; @@ -33,7 +34,6 @@ export class DashNav extends PureComponent { constructor(props: Props) { super(props); - this.playlistSrv = this.props.$injector.get('playlistSrv'); } @@ -123,16 +123,14 @@ export class DashNav extends PureComponent { }); }; - render() { - const { dashboard, isFullscreen, editview, onAddPanel } = this.props; - const { canStar, canSave, canShare, folderTitle, showSettings, isStarred } = dashboard.meta; - const { snapshot } = dashboard; + renderDashboardTitleSearchButton() { + const { dashboard } = this.props; + const folderTitle = dashboard.meta.folderTitle; const haveFolder = dashboard.meta.folderId > 0; - const snapshotUrl = snapshot && snapshot.originalUrl; return ( -
+ <> -
+ + ); + } + + renderPanelFullscreeMode() { + const { fullscreenPanel } = this.props; + + return ( +
+ +
+ + +
+
+ ); + } + + render() { + const { dashboard, onAddPanel, fullscreenPanel } = this.props; + const { canStar, canSave, canShare, showSettings, isStarred } = dashboard.meta; + const { snapshot } = dashboard; + + const snapshotUrl = snapshot && snapshot.originalUrl; + + return ( +
+ {!fullscreenPanel && this.renderDashboardTitleSearchButton()} + {fullscreenPanel && this.renderPanelFullscreeMode()} {this.playlistSrv.isPlaying && (
@@ -228,17 +256,6 @@ export class DashNav extends PureComponent {
(this.timePickerEl = element)} /> - - {(isFullscreen || editview) && ( -
- -
- )}
); } diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx index 27118e297b5..724f3a625c0 100644 --- a/public/app/features/dashboard/containers/DashboardPage.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.tsx @@ -238,7 +238,7 @@ export class DashboardPage extends PureComponent { render() { const { dashboard, editview, $injector, isInitSlow, initError } = this.props; - const { isSettingsOpening, isEditing, isFullscreen, scrollTop } = this.state; + const { isSettingsOpening, isEditing, isFullscreen, scrollTop, fullscreenPanel } = this.state; if (!dashboard) { if (isInitSlow) { @@ -266,6 +266,7 @@ export class DashboardPage extends PureComponent { editview={editview} $injector={$injector} onAddPanel={this.onAddPanel} + fullscreenPanel={fullscreenPanel} />
diff --git a/public/sass/components/_navbar.scss b/public/sass/components/_navbar.scss index 0cfa314a985..5215af41dcc 100644 --- a/public/sass/components/_navbar.scss +++ b/public/sass/components/_navbar.scss @@ -1,6 +1,6 @@ .navbar { position: relative; - padding-left: 40px; + padding-left: 20px; z-index: $zindex-navbar-fixed; height: $navbarHeight; padding-right: 20px; @@ -179,3 +179,66 @@ } } } + +.navbar-edit { + display: flex; + height: $navbarHeight; + align-items: center; + padding-left: 7px; + flex-grow: 1; +} + +.navbar-edit__back-btn { + background: transparent; + border: 2px solid $white; + border-radius: 50%; + width: 34px; + height: 34px; + margin-right: 7px; + + i { + font-size: $font-size-lg; + } +} + +.navbar-edit__input-wraper { + position: relative; + display: flex; + align-items: center; + flex-grow: 1; + + &:hover { + i { + opacity: 1; + } + + .navbar-edit__input { + background: $input-bg; + flex-grow: 1; + @include form-control-focus(); + } + } + + i { + left: -25px; + position: relative; + color: $text-color-weak; + opacity: 0; + transition: 200ms opacity ease-in-out; + } +} + +.navbar-edit__input { + background: transparent; + transition: 200ms background ease-in-out; + width: auto; + font-size: $font-size-lg; + height: $gf-form-input-height; + padding: $input-padding-y $input-padding-x; + flex-grow: 1; + + &:focus { + @include form-control-focus(); + background: $input-bg; + } +} diff --git a/public/sass/components/_panel_editor.scss b/public/sass/components/_panel_editor.scss index b791231a242..e533681d672 100644 --- a/public/sass/components/_panel_editor.scss +++ b/public/sass/components/_panel_editor.scss @@ -86,6 +86,10 @@ .panel-editor-container__panel { margin: 0 $dashboard-padding; } + + .panel-title-text { + visibility: hidden; + } } .panel-editor-container__resizer { From 2be60887cad49bfeaf37ed344e4981017f295c92 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 7 Feb 2019 16:27:40 +0100 Subject: [PATCH 03/14] adds usage stats for sessions --- pkg/infra/usagestats/service.go | 8 ++++---- pkg/infra/usagestats/usage_stats.go | 9 +++++++++ pkg/infra/usagestats/usage_stats_test.go | 8 +++++++- pkg/models/stats.go | 1 + pkg/services/sqlstore/stats.go | 3 ++- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/pkg/infra/usagestats/service.go b/pkg/infra/usagestats/service.go index f853c03302d..c2bf0d06349 100644 --- a/pkg/infra/usagestats/service.go +++ b/pkg/infra/usagestats/service.go @@ -5,7 +5,7 @@ import ( "time" "github.com/grafana/grafana/pkg/bus" - "github.com/grafana/grafana/pkg/services/auth" + "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/social" "github.com/grafana/grafana/pkg/log" @@ -20,9 +20,9 @@ func init() { } type UsageStatsService struct { - Cfg *setting.Cfg `inject:""` - TokenService *auth.UserAuthTokenService `inject:""` - Bus bus.Bus `inject:""` + Cfg *setting.Cfg `inject:""` + Bus bus.Bus `inject:""` + SQLStore *sqlstore.SqlStore `inject:""` oauthProviders map[string]bool } diff --git a/pkg/infra/usagestats/usage_stats.go b/pkg/infra/usagestats/usage_stats.go index b0dc52ccd8b..b54de124335 100644 --- a/pkg/infra/usagestats/usage_stats.go +++ b/pkg/infra/usagestats/usage_stats.go @@ -59,6 +59,15 @@ func (uss *UsageStatsService) sendUsageStats(oauthProviders map[string]bool) { metrics["stats.provisioned_dashboards.count"] = statsQuery.Result.ProvisionedDashboards metrics["stats.snapshots.count"] = statsQuery.Result.Snapshots metrics["stats.teams.count"] = statsQuery.Result.Teams + metrics["stats.total_sessions.count"] = statsQuery.Result.Sessions + + userCount := statsQuery.Result.Users + avgSessionsPerUser := statsQuery.Result.Sessions + if userCount != 0 { + avgSessionsPerUser = avgSessionsPerUser / userCount + } + + metrics["stats.avg_sessions_per_user.count"] = avgSessionsPerUser dsStats := models.GetDataSourceStatsQuery{} if err := uss.Bus.Dispatch(&dsStats); err != nil { diff --git a/pkg/infra/usagestats/usage_stats_test.go b/pkg/infra/usagestats/usage_stats_test.go index dd45e96f256..d343ed52b93 100644 --- a/pkg/infra/usagestats/usage_stats_test.go +++ b/pkg/infra/usagestats/usage_stats_test.go @@ -15,6 +15,7 @@ import ( "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" + "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" . "github.com/smartystreets/goconvey/convey" ) @@ -22,7 +23,8 @@ import ( func TestMetrics(t *testing.T) { Convey("Test send usage stats", t, func() { uss := &UsageStatsService{ - Bus: bus.New(), + Bus: bus.New(), + SQLStore: sqlstore.InitTestDB(t), } var getSystemStatsQuery *models.GetSystemStatsQuery @@ -43,6 +45,7 @@ func TestMetrics(t *testing.T) { ProvisionedDashboards: 12, Snapshots: 13, Teams: 14, + Sessions: 15, } getSystemStatsQuery = query return nil @@ -226,6 +229,8 @@ func TestMetrics(t *testing.T) { So(metrics.Get("stats.provisioned_dashboards.count").MustInt(), ShouldEqual, getSystemStatsQuery.Result.ProvisionedDashboards) So(metrics.Get("stats.snapshots.count").MustInt(), ShouldEqual, getSystemStatsQuery.Result.Snapshots) So(metrics.Get("stats.teams.count").MustInt(), ShouldEqual, getSystemStatsQuery.Result.Teams) + So(metrics.Get("stats.total_sessions.count").MustInt64(), ShouldEqual, 15) + So(metrics.Get("stats.avg_sessions_per_user.count").MustInt64(), ShouldEqual, 5) So(metrics.Get("stats.ds."+models.DS_ES+".count").MustInt(), ShouldEqual, 9) So(metrics.Get("stats.ds."+models.DS_PROMETHEUS+".count").MustInt(), ShouldEqual, 10) @@ -251,6 +256,7 @@ func TestMetrics(t *testing.T) { So(metrics.Get("stats.auth_enabled.oauth_grafana_com.count").MustInt(), ShouldEqual, 1) So(metrics.Get("stats.packaging.deb.count").MustInt(), ShouldEqual, 1) + }) }) diff --git a/pkg/models/stats.go b/pkg/models/stats.go index d3e145dedf4..00f881f3c59 100644 --- a/pkg/models/stats.go +++ b/pkg/models/stats.go @@ -15,6 +15,7 @@ type SystemStats struct { FolderPermissions int64 Folders int64 ProvisionedDashboards int64 + Sessions int64 } type DataSourceStats struct { diff --git a/pkg/services/sqlstore/stats.go b/pkg/services/sqlstore/stats.go index 2cec86e7239..4c6d6c21221 100644 --- a/pkg/services/sqlstore/stats.go +++ b/pkg/services/sqlstore/stats.go @@ -74,7 +74,8 @@ func GetSystemStats(query *m.GetSystemStatsQuery) error { sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("dashboard_provisioning") + `) AS provisioned_dashboards,`) sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("dashboard_snapshot") + `) AS snapshots,`) - sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("team") + `) AS teams`) + sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("team") + `) AS teams,`) + sb.Write(`(SELECT COUNT(id) FROM ` + dialect.Quote("user_auth_token") + `) AS sessions`) var stats m.SystemStats _, err := x.SQL(sb.GetSqlString(), sb.params...).Get(&stats) From 0f96cf866272ef72a016b0d1c9b5225037d73a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 7 Feb 2019 19:06:51 +0100 Subject: [PATCH 04/14] slight tweaks --- public/sass/components/_navbar.scss | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/public/sass/components/_navbar.scss b/public/sass/components/_navbar.scss index 5215af41dcc..0096810798a 100644 --- a/public/sass/components/_navbar.scss +++ b/public/sass/components/_navbar.scss @@ -41,7 +41,7 @@ .panel-in-fullscreen { .navbar { - padding-left: 15px; + padding-left: 20px; } .navbar-button--add-panel, @@ -190,14 +190,22 @@ .navbar-edit__back-btn { background: transparent; - border: 2px solid $white; + border: 2px solid $text-color; border-radius: 50%; width: 34px; height: 34px; - margin-right: 7px; + transition: transform 0.1s ease 0.1s; + color: $text-color; i { font-size: $font-size-lg; + position: relative; + top: 2px; + } + + &:hover { + color: $text-color-strong; + border-color: $text-color-strong; } } From f38e64cc5da68bee5395004f5fb0cfd26cecaf02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 10 Feb 2019 20:01:22 +0100 Subject: [PATCH 05/14] Navbar back button, no title edit this time --- .../dashboard/components/DashNav/DashNav.tsx | 30 ++++++------- .../dashboard/containers/DashboardPage.tsx | 3 +- public/sass/components/_navbar.scss | 44 ------------------- public/sass/components/_panel_editor.scss | 4 +- 4 files changed, 18 insertions(+), 63 deletions(-) diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index 8560b3bfbba..6db07b5d42e 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -9,16 +9,16 @@ import { PlaylistSrv } from 'app/features/playlist/playlist_srv'; // Components import { DashNavButton } from './DashNavButton'; +import { Tooltip } from '@grafana/ui'; // State import { updateLocation } from 'app/core/actions'; // Types -import { DashboardModel, PanelModel } from '../../state'; +import { DashboardModel } from '../../state'; export interface Props { dashboard: DashboardModel; - fullscreenPanel?: PanelModel; editview: string; isEditing: boolean; isFullscreen: boolean; @@ -133,7 +133,7 @@ export class DashNav extends PureComponent { <>
- + {!this.isInFullscreenOrSettings && } {haveFolder && {folderTitle} / } {dashboard.title} @@ -144,24 +144,24 @@ export class DashNav extends PureComponent { ); } - renderPanelFullscreeMode() { - const { fullscreenPanel } = this.props; + get isInFullscreenOrSettings() { + return this.props.editview || this.props.isFullscreen; + } + renderBackButton() { return (
- -
- - -
+ + +
); } render() { - const { dashboard, onAddPanel, fullscreenPanel } = this.props; + const { dashboard, onAddPanel } = this.props; const { canStar, canSave, canShare, showSettings, isStarred } = dashboard.meta; const { snapshot } = dashboard; @@ -169,8 +169,8 @@ export class DashNav extends PureComponent { return (
- {!fullscreenPanel && this.renderDashboardTitleSearchButton()} - {fullscreenPanel && this.renderPanelFullscreeMode()} + {this.isInFullscreenOrSettings && this.renderBackButton()} + {this.renderDashboardTitleSearchButton()} {this.playlistSrv.isPlaying && (
diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx index 724f3a625c0..27118e297b5 100644 --- a/public/app/features/dashboard/containers/DashboardPage.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.tsx @@ -238,7 +238,7 @@ export class DashboardPage extends PureComponent { render() { const { dashboard, editview, $injector, isInitSlow, initError } = this.props; - const { isSettingsOpening, isEditing, isFullscreen, scrollTop, fullscreenPanel } = this.state; + const { isSettingsOpening, isEditing, isFullscreen, scrollTop } = this.state; if (!dashboard) { if (isInitSlow) { @@ -266,7 +266,6 @@ export class DashboardPage extends PureComponent { editview={editview} $injector={$injector} onAddPanel={this.onAddPanel} - fullscreenPanel={fullscreenPanel} />
diff --git a/public/sass/components/_navbar.scss b/public/sass/components/_navbar.scss index 0096810798a..ce0fb45051e 100644 --- a/public/sass/components/_navbar.scss +++ b/public/sass/components/_navbar.scss @@ -47,9 +47,6 @@ .navbar-button--add-panel, .navbar-button--star, .navbar-button--tv, - .navbar-page-btn .fa-caret-down { - display: none; - } .navbar-buttons--close { display: flex; @@ -185,7 +182,6 @@ height: $navbarHeight; align-items: center; padding-left: 7px; - flex-grow: 1; } .navbar-edit__back-btn { @@ -209,44 +205,4 @@ } } -.navbar-edit__input-wraper { - position: relative; - display: flex; - align-items: center; - flex-grow: 1; - &:hover { - i { - opacity: 1; - } - - .navbar-edit__input { - background: $input-bg; - flex-grow: 1; - @include form-control-focus(); - } - } - - i { - left: -25px; - position: relative; - color: $text-color-weak; - opacity: 0; - transition: 200ms opacity ease-in-out; - } -} - -.navbar-edit__input { - background: transparent; - transition: 200ms background ease-in-out; - width: auto; - font-size: $font-size-lg; - height: $gf-form-input-height; - padding: $input-padding-y $input-padding-x; - flex-grow: 1; - - &:focus { - @include form-control-focus(); - background: $input-bg; - } -} diff --git a/public/sass/components/_panel_editor.scss b/public/sass/components/_panel_editor.scss index e533681d672..b1d828069fb 100644 --- a/public/sass/components/_panel_editor.scss +++ b/public/sass/components/_panel_editor.scss @@ -87,8 +87,8 @@ margin: 0 $dashboard-padding; } - .panel-title-text { - visibility: hidden; + .search-container { + left: 0 !important; } } From 9565e48f03535966bef099f4a19a3f6e418221c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Feb 2019 09:34:14 +0100 Subject: [PATCH 06/14] Fixed issue where double clicking on back button closes sidemenu --- public/app/core/components/sidemenu/SideMenu.test.tsx | 10 ++++++++++ public/app/core/components/sidemenu/SideMenu.tsx | 7 +++++++ public/app/core/reducers/location.ts | 2 ++ public/app/store/configureStore.ts | 4 ++-- public/app/types/location.ts | 1 + public/sass/components/_search.scss | 2 +- 6 files changed, 23 insertions(+), 3 deletions(-) diff --git a/public/app/core/components/sidemenu/SideMenu.test.tsx b/public/app/core/components/sidemenu/SideMenu.test.tsx index 2a262adca5a..2286787d777 100644 --- a/public/app/core/components/sidemenu/SideMenu.test.tsx +++ b/public/app/core/components/sidemenu/SideMenu.test.tsx @@ -8,6 +8,16 @@ jest.mock('../../app_events', () => ({ emit: jest.fn(), })); +jest.mock('app/store/store', () => ({ + store: { + getState: jest.fn().mockReturnValue({ + location: { + lastUpdated: 0, + } + }) + } +})); + jest.mock('app/core/services/context_srv', () => ({ contextSrv: { sidemenu: true, diff --git a/public/app/core/components/sidemenu/SideMenu.tsx b/public/app/core/components/sidemenu/SideMenu.tsx index fd3e0d95564..29ef0fed069 100644 --- a/public/app/core/components/sidemenu/SideMenu.tsx +++ b/public/app/core/components/sidemenu/SideMenu.tsx @@ -3,9 +3,16 @@ import appEvents from '../../app_events'; import { contextSrv } from 'app/core/services/context_srv'; import TopSection from './TopSection'; import BottomSection from './BottomSection'; +import { store } from 'app/store/store'; export class SideMenu extends PureComponent { toggleSideMenu = () => { + // ignore if we just made a location change, stops hiding sidemenu on double clicks of back button + const timeSinceLocationChanged = new Date().getTime() - store.getState().location.lastUpdated; + if (timeSinceLocationChanged < 1000) { + return; + } + contextSrv.toggleSideMenu(); appEvents.emit('toggle-sidemenu'); }; diff --git a/public/app/core/reducers/location.ts b/public/app/core/reducers/location.ts index c038ab53c9f..dff1ac8f5c1 100644 --- a/public/app/core/reducers/location.ts +++ b/public/app/core/reducers/location.ts @@ -9,6 +9,7 @@ export const initialState: LocationState = { query: {}, routeParams: {}, replace: false, + lastUpdated: 0, }; export const locationReducer = (state = initialState, action: Action): LocationState => { @@ -28,6 +29,7 @@ export const locationReducer = (state = initialState, action: Action): LocationS query: { ...query }, routeParams: routeParams || state.routeParams, replace: replace === true, + lastUpdated: new Date().getTime(), }; } } diff --git a/public/app/store/configureStore.ts b/public/app/store/configureStore.ts index e2c33523271..2638587e96d 100644 --- a/public/app/store/configureStore.ts +++ b/public/app/store/configureStore.ts @@ -1,6 +1,6 @@ import { createStore, applyMiddleware, compose, combineReducers } from 'redux'; import thunk from 'redux-thunk'; -import { createLogger } from 'redux-logger'; +// import { createLogger } from 'redux-logger'; import sharedReducers from 'app/core/reducers'; import alertingReducers from 'app/features/alerting/state/reducers'; import teamsReducers from 'app/features/teams/state/reducers'; @@ -41,7 +41,7 @@ export function configureStore() { if (process.env.NODE_ENV !== 'production') { // DEV builds we had the logger middleware - setStore(createStore(rootReducer, {}, composeEnhancers(applyMiddleware(thunk, createLogger())))); + setStore(createStore(rootReducer, {}, composeEnhancers(applyMiddleware(thunk)))); } else { setStore(createStore(rootReducer, {}, composeEnhancers(applyMiddleware(thunk)))); } diff --git a/public/app/types/location.ts b/public/app/types/location.ts index a47ef05d2be..4730f9d6ed7 100644 --- a/public/app/types/location.ts +++ b/public/app/types/location.ts @@ -15,6 +15,7 @@ export interface LocationState { query: UrlQueryMap; routeParams: UrlQueryMap; replace: boolean; + lastUpdated: number; } export type UrlQueryValue = string | number | boolean | string[] | number[] | boolean[]; diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss index daad8fd10da..eba03283510 100644 --- a/public/sass/components/_search.scss +++ b/public/sass/components/_search.scss @@ -21,9 +21,9 @@ // Search .search-field-wrapper { width: 100%; + height: $navbarHeight; display: flex; background-color: $navbarBackground; - box-shadow: $navbarShadow; position: relative; & > input { From 58e57a1669e8c2f2d0f2036ffed68d61f5069de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Feb 2019 09:46:56 +0100 Subject: [PATCH 07/14] removed unused directive --- public/app/core/core.ts | 1 - public/app/core/directives/dash_class.ts | 39 ------------------------ 2 files changed, 40 deletions(-) delete mode 100644 public/app/core/directives/dash_class.ts diff --git a/public/app/core/core.ts b/public/app/core/core.ts index 1f289fc4b27..80987b8fc88 100644 --- a/public/app/core/core.ts +++ b/public/app/core/core.ts @@ -1,4 +1,3 @@ -import './directives/dash_class'; import './directives/dropdown_typeahead'; import './directives/autofill_event_fix'; import './directives/metric_segment'; diff --git a/public/app/core/directives/dash_class.ts b/public/app/core/directives/dash_class.ts deleted file mode 100644 index 1fb93d29cf3..00000000000 --- a/public/app/core/directives/dash_class.ts +++ /dev/null @@ -1,39 +0,0 @@ -import $ from 'jquery'; -import _ from 'lodash'; -import coreModule from '../core_module'; - -/** @ngInject */ -function dashClass($timeout) { - return { - link: ($scope, elem) => { - const body = $('body'); - - $scope.ctrl.dashboard.events.on('view-mode-changed', panel => { - console.log('view-mode-changed', panel.fullscreen); - if (panel.fullscreen) { - body.addClass('panel-in-fullscreen'); - } else { - $timeout(() => { - body.removeClass('panel-in-fullscreen'); - }); - } - }); - - body.toggleClass('panel-in-fullscreen', $scope.ctrl.dashboard.meta.fullscreen === true); - - $scope.$watch('ctrl.dashboardViewState.state.editview', newValue => { - if (newValue) { - elem.toggleClass('dashboard-page--settings-opening', _.isString(newValue)); - setTimeout(() => { - elem.toggleClass('dashboard-page--settings-open', _.isString(newValue)); - }, 10); - } else { - elem.removeClass('dashboard-page--settings-opening'); - elem.removeClass('dashboard-page--settings-open'); - } - }); - }, - }; -} - -coreModule.directive('dashClass', dashClass); From 3bf0a5ffc68ff395300e8191443d3bd999bcf97d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Feb 2019 10:01:43 +0100 Subject: [PATCH 08/14] Fixed issue with logs graph not showing level names --- public/app/core/logs_model.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/public/app/core/logs_model.ts b/public/app/core/logs_model.ts index abcd5563bd0..2cde5379448 100644 --- a/public/app/core/logs_model.ts +++ b/public/app/core/logs_model.ts @@ -340,6 +340,11 @@ export function makeSeriesForLogs(rows: LogRowModel[], intervalMs: number): Time return a[1] - b[1]; }); - return { datapoints: series.datapoints, target: series.alias, color: series.color }; + return { + datapoints: series.datapoints, + target: series.alias, + alias: series.alias, + color: series.color + }; }); } From e75e69a709c8fa4d4818f761f3c99edc21ede66e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Feb 2019 10:27:04 +0100 Subject: [PATCH 09/14] Commented out the Loki dashboard query editor --- .../loki/components/LokiQueryEditor.tsx | 85 ++++++++++--------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx b/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx index a1b9e7a5df9..14fe046e098 100644 --- a/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx +++ b/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx @@ -2,61 +2,65 @@ import React, { PureComponent } from 'react'; // Components -import { Select, SelectOptionItem } from '@grafana/ui'; +// import { Select, SelectOptionItem } from '@grafana/ui'; // Types import { QueryEditorProps } from '@grafana/ui/src/types'; import { LokiDatasource } from '../datasource'; import { LokiQuery } from '../types'; -import { LokiQueryField } from './LokiQueryField'; +// import { LokiQueryField } from './LokiQueryField'; type Props = QueryEditorProps; -interface State { - query: LokiQuery; -} +// interface State { +// query: LokiQuery; +// } export class LokiQueryEditor extends PureComponent { - state: State = { - query: this.props.query, - }; - - onRunQuery = () => { - const { query } = this.state; - - this.props.onChange(query); - this.props.onRunQuery(); - }; - - onFieldChange = (query: LokiQuery, override?) => { - this.setState({ - query: { - ...this.state.query, - expr: query.expr, - }, - }); - }; - - onFormatChanged = (option: SelectOptionItem) => { - this.props.onChange({ - ...this.state.query, - resultFormat: option.value, - }); - }; + // state: State = { + // query: this.props.query, + // }; + // + // onRunQuery = () => { + // const { query } = this.state; + // + // this.props.onChange(query); + // this.props.onRunQuery(); + // }; + // + // onFieldChange = (query: LokiQuery, override?) => { + // this.setState({ + // query: { + // ...this.state.query, + // expr: query.expr, + // }, + // }); + // }; + // + // onFormatChanged = (option: SelectOptionItem) => { + // this.props.onChange({ + // ...this.state.query, + // resultFormat: option.value, + // }); + // }; render() { - const { query } = this.state; - const { datasource } = this.props; - const formatOptions: SelectOptionItem[] = [ - { label: 'Time Series', value: 'time_series' }, - { label: 'Table', value: 'table' }, - ]; - - query.resultFormat = query.resultFormat || 'time_series'; - const currentFormat = formatOptions.find(item => item.value === query.resultFormat); + // const { query } = this.state; + // const { datasource } = this.props; + // const formatOptions: SelectOptionItem[] = [ + // { label: 'Time Series', value: 'time_series' }, + // { label: 'Table', value: 'table' }, + // ]; + // + // query.resultFormat = query.resultFormat || 'time_series'; + // const currentFormat = formatOptions.find(item => item.value === query.resultFormat); return (
+
+
Loki is currently not supported as dashboard data source. We are working on it!
+
+ {/* {
+ */}
); } From 2c8c4729a8ad13a44b7fdef1c570cc940777fb10 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 11 Feb 2019 10:47:03 +0100 Subject: [PATCH 10/14] changelog: adds note about closing #15288 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30f4961343b..e6971f7952c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * **MSSQL**: Timerange are now passed for template variable queries [#13324](https://github.com/grafana/grafana/issues/13324), thx [@thatsparesh](https://github.com/thatsparesh) * **Annotations**: Support PATCH verb in annotations http api [#12546](https://github.com/grafana/grafana/issues/12546), thx [@SamuelToh](https://github.com/SamuelToh) * **Templating**: Add json formatting to variable interpolation [#15291](https://github.com/grafana/grafana/issues/15291), thx [@mtanda](https://github.com/mtanda) +* **Login**: Anonymous usage stats for token auth [#15288](https://github.com/grafana/grafana/issues/15288) ### 6.0.0-beta1 fixes From 784d4fb70d676cf3f57fda39631e9c5bc451bdc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Feb 2019 10:57:32 +0100 Subject: [PATCH 11/14] Update README.md --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 658f1e34257..2cb8bfee306 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,18 @@ Grafana is an open source, feature rich metrics dashboard and graph editor for Graphite, Elasticsearch, OpenTSDB, Prometheus and InfluxDB. +![](https://www.grafanacon.org/2019/images/grafanacon_la_nav-logo.png) + +Join us Feb 25-26 in Los Angeles, California for GrafanaCon - a two-day event with talks focused on Grafana and the surrounding open source monitoring ecosystem. Get deep dives into Loki, the Explore workflow and all of the new features of Grafana 6, plus participate in hands on workshops to help you get the most out of your data. + +Time is running out - grab your ticket now! http://grafanacon.org + + ## Installation -Head to [docs.grafana.org](http://docs.grafana.org/installation/) and [download](https://grafana.com/get) -the latest release. - -If you have any problems please read the [troubleshooting guide](http://docs.grafana.org/installation/troubleshooting/). +Head to [docs.grafana.org](http://docs.grafana.org/installation/) for documentation or [download](https://grafana.com/get) to get the latest release. ## Documentation & Support Be sure to read the [getting started guide](http://docs.grafana.org/guides/gettingstarted/) and the other feature guides. From f39fef2a027879d5c7385fc8b8e5515a938a6487 Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Mon, 11 Feb 2019 11:03:24 +0100 Subject: [PATCH 12/14] Clear visualization picker search on picker close --- package.json | 1 + public/app/core/components/Animations/FadeIn.tsx | 10 ++++++++-- .../dashboard/panel_editor/VisualizationTab.tsx | 6 +++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 5ac751ced3f..fae51a1d856 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@types/react-dom": "^16.0.9", "@types/react-grid-layout": "^0.16.6", "@types/react-select": "^2.0.4", + "@types/react-transition-group": "^2.0.15", "@types/react-virtualized": "^9.18.12", "angular-mocks": "1.6.6", "autoprefixer": "^6.4.0", diff --git a/public/app/core/components/Animations/FadeIn.tsx b/public/app/core/components/Animations/FadeIn.tsx index ea9a92d5f0f..d667b54261e 100644 --- a/public/app/core/components/Animations/FadeIn.tsx +++ b/public/app/core/components/Animations/FadeIn.tsx @@ -1,11 +1,12 @@ import React, { FC } from 'react'; -import Transition from 'react-transition-group/Transition'; +import Transition, { ExitHandler } from 'react-transition-group/Transition'; interface Props { duration: number; children: JSX.Element; in: boolean; unmountOnExit?: boolean; + onExited?: ExitHandler; } export const FadeIn: FC = props => { @@ -22,7 +23,12 @@ export const FadeIn: FC = props => { }; return ( - + {state => (
{ } } + clearQuery = () => { + this.setState({ searchQuery: '' }); + }; + onPanelOptionsChanged = (options: any) => { this.props.panel.updateOptions(options); this.forceUpdate(); @@ -241,7 +245,7 @@ export class VisualizationTab extends PureComponent { setScrollTop={this.setScrollTop} > <> - + Date: Mon, 11 Feb 2019 11:11:21 +0100 Subject: [PATCH 13/14] should be able to navigate to folder with only uid --- public/app/routes/routes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index e0029cf2464..4c9c5fd5304 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -150,8 +150,8 @@ export function setupAngularRoutes($routeProvider, $locationProvider) { controllerAs: 'ctrl', }) .when('/dashboards/f/:uid', { - templateUrl: 'public/app/features/dashboard/partials/folder_dashboards.html', - controller: 'FolderDashboardsCtrl', + templateUrl: 'public/app/features/folders/partials/folder_dashboards.html', + controller: FolderDashboardsCtrl, controllerAs: 'ctrl', }) .when('/explore', { From b780b6377ae2cbc7f22b7245fb3c7b7145e7527a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 11 Feb 2019 11:17:23 +0100 Subject: [PATCH 14/14] Fixed missing time axis on graph due to width not being passed --- public/app/features/explore/Explore.tsx | 1 + public/app/features/explore/Logs.tsx | 3 +++ public/app/features/explore/LogsContainer.tsx | 3 +++ 3 files changed, 7 insertions(+) diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index a28776d813a..aca8f033fb3 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -220,6 +220,7 @@ export class Explore extends React.PureComponent { {supportsTable && } {supportsLogs && ( { range, scanning, scanRange, + width, } = this.props; if (!data) { @@ -215,6 +217,7 @@ export default class Logs extends PureComponent { { @@ -46,6 +47,7 @@ export class LogsContainer extends PureComponent { showingLogs, scanning, scanRange, + width, } = this.props; return ( @@ -63,6 +65,7 @@ export class LogsContainer extends PureComponent { range={range} scanning={scanning} scanRange={scanRange} + width={width} /> );