From 4c891e37585ff8c08a2a778c5901f7af97eececf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 8 Nov 2016 15:06:07 +0100 Subject: [PATCH 1/3] docs(): minor spelling fix --- docs/sources/alerting/notifications.md | 55 ++++++++------------------ 1 file changed, 16 insertions(+), 39 deletions(-) diff --git a/docs/sources/alerting/notifications.md b/docs/sources/alerting/notifications.md index f274001b9d6..455d61cf535 100644 --- a/docs/sources/alerting/notifications.md +++ b/docs/sources/alerting/notifications.md @@ -25,31 +25,12 @@ to add and configure a `notification` object. This is done from the Alerting/Not On the notifications list page hit the `New Notification` button to go the the page where you can configure and setup a new notification. -You you specify name and type, and type specific options. You can also test the notification to make +You specify name and type, and type specific options. You can also test the notification to make sure it's working and setup correctly. - - - - - - - - - - - - - - - - - - - ### Send on all alerts -This option will make this notification used for all alert rules, existing and new. +When checked this option will make this notification used for all alert rules, existing and new. ## Supported notification types @@ -61,12 +42,25 @@ To enable email notification you have to setup [SMTP settings](/installation/con in the Grafana config. Email notification will upload an image of the alert graph to an external image destination if available or fallback on attaching the image in the email. +### Slack + +{{< imgbox max-width="40%" img="/img/docs/v4/slack_notification.png" caption="Alerting Slack Notification" >}} + +To set up slack you need to configure an incoming webhook url at slack. You can follow their guide for how +to do that https://api.slack.com/incoming-webhooks If you want to include screenshots of the firing alerts +in the slack messages you have to configure the [external image destination](#external-image-store) in Grafana. + +Setting | Description +---------- | ----------- +Recipient | allows you to override the slack recipient. +Mention | make it possible to include a mention in the slack notification sent by Grafana. Ex @here or @channel + ### Webhook The webhook notification is a simple way to send information about an state change over HTTP to a custom endpoint. Using this notification you could integrated Grafana into any system you choose, by yourself. -Example json schema: +Example json body: ```json { "title": "My alert", @@ -85,19 +79,6 @@ Example json schema: } ``` -### Slack - -{{< imgbox max-width="40%" img="/img/docs/v4/slack_notification.png" caption="Alerting Slack Notification" >}} - -To set up slack you need to configure an incoming webhook url at slack. You can follow their guide for how -to do that https://api.slack.com/incoming-webhooks If you want to include screenshots of the firing alerts -in the slack messages you have to configure the [external image destination](#external-image-store) in Grafana. - -Setting | Description ----------- | ----------- -Recipient | allows you to override the slack recipient. -Mention | make it possible to include a mention in the slack notification sent by Grafana. Ex @here or @channel - ### PagerDuty To set up PagerDuty, all you have to do is to provide an api key. @@ -117,7 +98,3 @@ config file. This is not an optional requirement, you can get slack and email notifications without setting this up. - - - - From d92bb677dfd09e4f0882631d62310c2a956a07d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 8 Nov 2016 16:15:34 +0100 Subject: [PATCH 2/3] docs(): fix title --- docs/sources/alerting/rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/alerting/rules.md b/docs/sources/alerting/rules.md index 823637941da..a30bf885650 100644 --- a/docs/sources/alerting/rules.md +++ b/docs/sources/alerting/rules.md @@ -1,5 +1,5 @@ +++ -title = "Alerting Engine Rules Guide" +title = "Alerting Engine & Rules Guide" description = "Configuring Alert Rules" keywords = ["grafana", "alerting", "guide", "rules"] type = "docs" From fb57bf77daec82325198ea76f5a95f9054aa3c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 9 Nov 2016 10:41:39 +0100 Subject: [PATCH 3/3] ux(getting started): progress on getting started panel and persited help flag states, #6466 --- pkg/api/api.go | 3 + pkg/api/dtos/models.go | 27 ++++---- pkg/api/index.go | 1 + pkg/api/user.go | 31 ++++++++++ pkg/models/helpflags.go | 18 ++++++ pkg/models/user.go | 2 + pkg/services/sqlstore/migrations/user_mig.go | 4 ++ pkg/services/sqlstore/user.go | 21 ++++++- public/app/core/services/backend_srv.ts | 4 +- public/app/core/services/context_srv.ts | 1 + .../app/features/dashboard/row/add_panel.ts | 3 - .../app/features/dashboard/row/row_model.ts | 1 - .../plugins/panel/gettingstarted/module.html | 23 +++++-- .../plugins/panel/gettingstarted/module.ts | 39 ++++++++++-- public/dashboards/home.json | 12 +++- .../components/_panel_gettingstarted.scss | 62 +++---------------- 16 files changed, 165 insertions(+), 87 deletions(-) create mode 100644 pkg/models/helpflags.go diff --git a/pkg/api/api.go b/pkg/api/api.go index ed73f2dc76d..6ea5bcd7c95 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -113,6 +113,9 @@ func Register(r *macaron.Macaron) { r.Put("/password", bind(m.ChangeUserPasswordCommand{}), wrap(ChangeUserPassword)) r.Get("/quotas", wrap(GetUserQuotas)) + r.Put("/helpflags/:id", wrap(SetHelpFlag)) + // For dev purpose + r.Get("/helpflags/clear", wrap(ClearHelpFlags)) r.Get("/preferences", wrap(GetUserPreferences)) r.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateUserPreferences)) diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index 170a5a868fc..7f8d107d9f4 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -22,19 +22,20 @@ type LoginCommand struct { } type CurrentUser struct { - IsSignedIn bool `json:"isSignedIn"` - Id int64 `json:"id"` - Login string `json:"login"` - Email string `json:"email"` - Name string `json:"name"` - LightTheme bool `json:"lightTheme"` - OrgId int64 `json:"orgId"` - OrgName string `json:"orgName"` - OrgRole m.RoleType `json:"orgRole"` - IsGrafanaAdmin bool `json:"isGrafanaAdmin"` - GravatarUrl string `json:"gravatarUrl"` - Timezone string `json:"timezone"` - Locale string `json:"locale"` + IsSignedIn bool `json:"isSignedIn"` + Id int64 `json:"id"` + Login string `json:"login"` + Email string `json:"email"` + Name string `json:"name"` + LightTheme bool `json:"lightTheme"` + OrgId int64 `json:"orgId"` + OrgName string `json:"orgName"` + OrgRole m.RoleType `json:"orgRole"` + IsGrafanaAdmin bool `json:"isGrafanaAdmin"` + GravatarUrl string `json:"gravatarUrl"` + Timezone string `json:"timezone"` + Locale string `json:"locale"` + HelpFlags1 m.HelpFlags1 `json:"helpFlags1"` } type DashboardMeta struct { diff --git a/pkg/api/index.go b/pkg/api/index.go index 99a5f78f9c9..5bc4344a8ba 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -58,6 +58,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { LightTheme: prefs.Theme == "light", Timezone: prefs.Timezone, Locale: locale, + HelpFlags1: c.HelpFlags1, }, Settings: settings, AppUrl: appUrl, diff --git a/pkg/api/user.go b/pkg/api/user.go index f98eec02c40..715103aacbd 100644 --- a/pkg/api/user.go +++ b/pkg/api/user.go @@ -180,3 +180,34 @@ func SearchUsers(c *middleware.Context) Response { return Json(200, query.Result) } + +func SetHelpFlag(c *middleware.Context) Response { + flag := c.ParamsInt64(":id") + + bitmask := &c.HelpFlags1 + bitmask.AddFlag(m.HelpFlags1(flag)) + + cmd := m.SetUserHelpFlagCommand{ + UserId: c.UserId, + HelpFlags1: *bitmask, + } + + if err := bus.Dispatch(&cmd); err != nil { + return ApiError(500, "Failed to update help flag", err) + } + + return Json(200, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1}) +} + +func ClearHelpFlags(c *middleware.Context) Response { + cmd := m.SetUserHelpFlagCommand{ + UserId: c.UserId, + HelpFlags1: m.HelpFlags1(0), + } + + if err := bus.Dispatch(&cmd); err != nil { + return ApiError(500, "Failed to update help flag", err) + } + + return Json(200, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1}) +} diff --git a/pkg/models/helpflags.go b/pkg/models/helpflags.go new file mode 100644 index 00000000000..1bab730e265 --- /dev/null +++ b/pkg/models/helpflags.go @@ -0,0 +1,18 @@ +package models + +type HelpFlags1 uint64 + +const ( + HelpFlagGettingStartedPanelDismissed HelpFlags1 = 1 << iota + HelpFlagDashboardHelp1 +) + +func (f HelpFlags1) HasFlag(flag HelpFlags1) bool { return f&flag != 0 } +func (f *HelpFlags1) AddFlag(flag HelpFlags1) { *f |= flag } +func (f *HelpFlags1) ClearFlag(flag HelpFlags1) { *f &= ^flag } +func (f *HelpFlags1) ToggleFlag(flag HelpFlags1) { *f ^= flag } + +type SetUserHelpFlagCommand struct { + HelpFlags1 HelpFlags1 + UserId int64 +} diff --git a/pkg/models/user.go b/pkg/models/user.go index 1f99f866c86..8bfad7c2fb4 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -22,6 +22,7 @@ type User struct { Company string EmailVerified bool Theme string + HelpFlags1 HelpFlags1 IsAdmin bool OrgId int64 @@ -144,6 +145,7 @@ type SignedInUser struct { Email string ApiKeyId int64 IsGrafanaAdmin bool + HelpFlags1 HelpFlags1 } type UserProfileDTO struct { diff --git a/pkg/services/sqlstore/migrations/user_mig.go b/pkg/services/sqlstore/migrations/user_mig.go index 51db4d74857..67446f64d1a 100644 --- a/pkg/services/sqlstore/migrations/user_mig.go +++ b/pkg/services/sqlstore/migrations/user_mig.go @@ -88,4 +88,8 @@ func addUserMigrations(mg *Migrator) { })) mg.AddMigration("Drop old table user_v1", NewDropTableMigration("user_v1")) + + mg.AddMigration("Add column help_flags1 to user table", NewAddColumnMigration(userV2, &Column{ + Name: "help_flags1", Type: DB_BigInt, Nullable: false, Default: "0", + })) } diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go index bb21995f54a..b26a9153f55 100644 --- a/pkg/services/sqlstore/user.go +++ b/pkg/services/sqlstore/user.go @@ -28,6 +28,7 @@ func init() { bus.AddHandler("sql", DeleteUser) bus.AddHandler("sql", SetUsingOrg) bus.AddHandler("sql", UpdateUserPermissions) + bus.AddHandler("sql", SetUserHelpFlag) } func getOrgIdForNewUser(cmd *m.CreateUserCommand, sess *session) (int64, error) { @@ -207,7 +208,7 @@ func GetUserByEmail(query *m.GetUserByEmailQuery) error { if err != nil { return err } else if has == false { - return m.ErrUserNotFound + return m.ErrUserNotFound } query.Result = user @@ -308,6 +309,7 @@ func GetSignedInUser(query *m.GetSignedInUserQuery) error { u.email as email, u.login as login, u.name as name, + u.help_flags1 as help_flags1, org.name as org_name, org_user.role as org_role, org.id as org_id @@ -380,3 +382,20 @@ func UpdateUserPermissions(cmd *m.UpdateUserPermissionsCommand) error { return err }) } + +func SetUserHelpFlag(cmd *m.SetUserHelpFlagCommand) error { + return inTransaction2(func(sess *session) error { + + user := m.User{ + Id: cmd.UserId, + HelpFlags1: cmd.HelpFlags1, + Updated: time.Now(), + } + + if _, err := sess.Id(cmd.UserId).Cols("help_flags1").Update(&user); err != nil { + return err + } + + return nil + }) +} diff --git a/public/app/core/services/backend_srv.ts b/public/app/core/services/backend_srv.ts index 1e620e88216..9a5ec5d219b 100644 --- a/public/app/core/services/backend_srv.ts +++ b/public/app/core/services/backend_srv.ts @@ -74,7 +74,9 @@ export class BackendSrv { return this.$http(options).then(results => { if (options.method !== 'GET') { if (results && results.data.message) { - this.alertSrv.set(results.data.message, '', 'success', 3000); + if (options.showSuccessAlert !== false) { + this.alertSrv.set(results.data.message, '', 'success', 3000); + } } } return results.data; diff --git a/public/app/core/services/context_srv.ts b/public/app/core/services/context_srv.ts index ac00528db20..3d048e4f869 100644 --- a/public/app/core/services/context_srv.ts +++ b/public/app/core/services/context_srv.ts @@ -10,6 +10,7 @@ export class User { isSignedIn: any; orgRole: any; timezone: string; + helpFlags1: number; constructor() { if (config.bootData.user) { diff --git a/public/app/features/dashboard/row/add_panel.ts b/public/app/features/dashboard/row/add_panel.ts index 3e2dc9e31d8..189108d9b4a 100644 --- a/public/app/features/dashboard/row/add_panel.ts +++ b/public/app/features/dashboard/row/add_panel.ts @@ -84,11 +84,8 @@ export class AddPanelCtrl { var panel = { id: null, title: config.new_panel_title, - error: false, span: span < defaultSpan && span > 0 ? span : defaultSpan, - editable: true, type: panelPluginInfo.id, - isNew: true, }; this.rowCtrl.closeDropView(); diff --git a/public/app/features/dashboard/row/row_model.ts b/public/app/features/dashboard/row/row_model.ts index 0729d43bf0a..cbde701ccfe 100644 --- a/public/app/features/dashboard/row/row_model.ts +++ b/public/app/features/dashboard/row/row_model.ts @@ -19,7 +19,6 @@ export class DashboardRow { showTitle: false, titleSize: 'h6', height: 250, - isNew: false, repeat: null, repeatRowId: null, repeatIteration: null, diff --git a/public/app/plugins/panel/gettingstarted/module.html b/public/app/plugins/panel/gettingstarted/module.html index ac18a53bd4d..0c319a46d81 100644 --- a/public/app/plugins/panel/gettingstarted/module.html +++ b/public/app/plugins/panel/gettingstarted/module.html @@ -2,23 +2,34 @@
Getting Started with Grafana - +
-
    -
  • +
      +
    • Install Grafana
    • -
    • +
    • Create your first data source.
    • -
    • +
    • + + + Create your first data source. + +
    • +
    • - Create your first dashboard. + + Create your first dashboard. + +
    • diff --git a/public/app/plugins/panel/gettingstarted/module.ts b/public/app/plugins/panel/gettingstarted/module.ts index 178c378f04d..e78203647c0 100644 --- a/public/app/plugins/panel/gettingstarted/module.ts +++ b/public/app/plugins/panel/gettingstarted/module.ts @@ -2,14 +2,43 @@ import {PanelCtrl} from 'app/plugins/sdk'; -class GettingstartedPanelCtrl extends PanelCtrl { - static templateUrl = 'public/app/plugins/panel/gettingstarted/module.html'; +import {contextSrv} from 'app/core/core'; - /** @ngInject */ - constructor($scope, $injector) { +class GettingStartedPanelCtrl extends PanelCtrl { + static templateUrl = 'public/app/plugins/panel/gettingstarted/module.html'; + hasDatasources: boolean; + checksDone: boolean; + + /** @ngInject **/ + constructor($scope, $injector, private backendSrv, private datasourceSrv) { super($scope, $injector); + + /* tslint:disable */ + if (contextSrv.user.helpFlags1 & 1) { + this.row.removePanel(this.panel, false); + return; + } + /* tslint:enable */ + + var datasources = datasourceSrv.getMetricSources().filter(item => { + return item.meta.builtIn === false; + }); + + this.hasDatasources = datasources.length > 0; + this.checksDone = true; } + dismiss() { + this.row.removePanel(this.panel, false); + + this.backendSrv.request({ + method: 'PUT', + url: '/api/user/helpflags/1', + showSuccessAlert: false, + }).then(res => { + contextSrv.user.helpFlags1 = res.helpFlags1; + }); + } } -export {GettingstartedPanelCtrl, GettingstartedPanelCtrl as PanelCtrl} +export {GettingStartedPanelCtrl, GettingStartedPanelCtrl as PanelCtrl} diff --git a/public/dashboards/home.json b/public/dashboards/home.json index 393cbc5865c..0ea0af67055 100644 --- a/public/dashboards/home.json +++ b/public/dashboards/home.json @@ -9,6 +9,7 @@ "sharedCrosshair": false, "rows": [ { + "title": "Row title", "collapse": false, "editable": true, "height": "25px", @@ -24,9 +25,16 @@ "title": "", "transparent": true, "type": "text" + }, + { + "id": 8, + "links": [], + "span": 12, + "title": "", + "transparent": false, + "type": "gettingstarted" } - ], - "title": "New row" + ] }, { "collapse": false, diff --git a/public/sass/components/_panel_gettingstarted.scss b/public/sass/components/_panel_gettingstarted.scss index c2b3366d54d..fd92af59179 100644 --- a/public/sass/components/_panel_gettingstarted.scss +++ b/public/sass/components/_panel_gettingstarted.scss @@ -1,11 +1,3 @@ -ul.gettingstarted-flex-container { - display: flex; - justify-content: space-around; - flex-direction: row; - padding: 20px; - list-style-type: none; -} - .gettingstarted-flex-item { align-items: center; display: flex; @@ -19,14 +11,14 @@ ul.gettingstarted-flex-container { text-align: center; } -a.gettingstarted-blurb{ +.gettingstarted-blurb { @extend .gettingstarted-blurb-copy; color: $text-color; display: block; -} -a.gettingstarted-blurb:hover{ - text-decoration: underline; + &:hover{ + text-decoration: underline; + } } .gettingstarted-blurb-success { @@ -35,27 +27,11 @@ a.gettingstarted-blurb:hover{ text-decoration: line-through; } -a.gettingstarted-blurb-upcoming { +.gettingstarted-blurb-upcoming { @extend .gettingstarted-blurb-copy; color: $text-color-weak; } -.gettingstarted-icon-container { - height: 50px; -} - -.gettingstarted-icon-active { - color: $brand-primary; - -webkit-text-fill-color: transparent; - background: $brand-gradient; - -webkit-background-clip: text; - text-decoration:none; - font-size: 35px; - vertical-align: sub; - animation: iconPulse 500ms forwards 1s; - will-change: zoom; -} - .gettingstarted-icon-upcoming { color: $text-color-weak; text-decoration:none; @@ -70,7 +46,6 @@ a.gettingstarted-blurb-upcoming { vertical-align: sub; } - .dashlist-CTA-close-btn { float: right; padding: 0; @@ -86,25 +61,6 @@ a.gettingstarted-blurb-upcoming { } } -@keyframes iconPulse { - from { - zoom: 100%; - } - - 50% { - zoom: 102%; - } - - to { - zoom: 100%; - } -} - - -// ----- Progress Tracker ----- - -// ----- Variables ----- - // Colours $progress-color-dark: $panel-bg !default; $progress-color: $panel-bg !default; @@ -240,17 +196,16 @@ $ripple-color: rgba(0, 0, 0, 0.3) !default; // States .progress-step { - // Inactive - Default state @include progress-state($progress-color, null, #fff, $progress-color-grey-light, $progress-color-grey-dark); // Active state - &.is-active { + &.active { @include progress-state($progress-color); } // Complete state - &.is-complete { + &.completed { @include progress-state($progress-color-dark, $path-color: $progress-color-grey); } @@ -258,11 +213,8 @@ $ripple-color: rgba(0, 0, 0, 0.3) !default; &:hover { @include progress-state($progress-color-light); } - } - - // ----- Modifiers ----- // Center align markers and text