From 277c9ed462f7c6da1b5f7173296e360e74e64496 Mon Sep 17 00:00:00 2001 From: Jason Cooper Date: Mon, 6 Jul 2020 21:56:42 +0800 Subject: [PATCH] notify: add markdown support for wechat (#2309) * notify: add markdown support for wechat Signed-off-by: Jason Cooper * docs: update WeChat receiver configuration document Signed-off-by: Jason Cooper * fix: check WeChat msgType, apply default if not present Signed-off-by: Jason Cooper * chore: remove unnecessary comment Signed-off-by: Jason Cooper * fix: simplify msgType process Signed-off-by: Jason Cooper * docs: wechat configs document update Signed-off-by: Jason Cooper * fix: apply error message suggestions Signed-off-by: Jason Cooper * test: add test for regex Signed-off-by: Jason Cooper * fix: wechat message safe param Signed-off-by: Jason Cooper --- config/notifiers.go | 35 ++++++++++++++++++++++++++--------- config/notifiers_test.go | 15 +++++++++++++++ docs/configuration.md | 2 ++ notify/wechat/wechat.go | 30 +++++++++++++++++++----------- notify/wechat/wechat_test.go | 23 +++++++++++++++++++++++ 5 files changed, 85 insertions(+), 20 deletions(-) diff --git a/config/notifiers.go b/config/notifiers.go index d112e268..d7385dd6 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -396,21 +396,38 @@ type WechatConfig struct { HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` - APISecret Secret `yaml:"api_secret,omitempty" json:"api_secret,omitempty"` - CorpID string `yaml:"corp_id,omitempty" json:"corp_id,omitempty"` - Message string `yaml:"message,omitempty" json:"message,omitempty"` - APIURL *URL `yaml:"api_url,omitempty" json:"api_url,omitempty"` - ToUser string `yaml:"to_user,omitempty" json:"to_user,omitempty"` - ToParty string `yaml:"to_party,omitempty" json:"to_party,omitempty"` - ToTag string `yaml:"to_tag,omitempty" json:"to_tag,omitempty"` - AgentID string `yaml:"agent_id,omitempty" json:"agent_id,omitempty"` + APISecret Secret `yaml:"api_secret,omitempty" json:"api_secret,omitempty"` + CorpID string `yaml:"corp_id,omitempty" json:"corp_id,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` + APIURL *URL `yaml:"api_url,omitempty" json:"api_url,omitempty"` + ToUser string `yaml:"to_user,omitempty" json:"to_user,omitempty"` + ToParty string `yaml:"to_party,omitempty" json:"to_party,omitempty"` + ToTag string `yaml:"to_tag,omitempty" json:"to_tag,omitempty"` + AgentID string `yaml:"agent_id,omitempty" json:"agent_id,omitempty"` + MessageType string `yaml:"message_type,omitempty" json:"message_type,omitempty"` } +const wechatValidTypesRe = `^(text|markdown)$` + +var wechatTypeMatcher = regexp.MustCompile(wechatValidTypesRe) + // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *WechatConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultWechatConfig type plain WechatConfig - return unmarshal((*plain)(c)) + if err := unmarshal((*plain)(c)); err != nil { + return err + } + + if c.MessageType == "" { + c.MessageType = "text" + } + + if !wechatTypeMatcher.MatchString(c.MessageType) { + return errors.Errorf("WeChat message type %q does not match valid options %s", c.MessageType, wechatValidTypesRe) + } + + return nil } // OpsGenieConfig configures notifications via OpsGenie. diff --git a/config/notifiers_test.go b/config/notifiers_test.go index a7346fe0..2c4c8126 100644 --- a/config/notifiers_test.go +++ b/config/notifiers_test.go @@ -590,6 +590,21 @@ func TestOpsgenieTypeMatcher(t *testing.T) { } } +func TestWeChatTypeMatcher(t *testing.T) { + good := []string{"text", "markdown"} + for _, g := range good { + if !wechatTypeMatcher.MatchString(g) { + t.Fatalf("failed to match with %s", g) + } + } + bad := []string{"TEXT", "MarkDOwn"} + for _, b := range bad { + if wechatTypeMatcher.MatchString(b) { + t.Errorf("mistakenly match with %s", b) + } + } +} + func newBoolPointer(b bool) *bool { return &b } diff --git a/docs/configuration.md b/docs/configuration.md index 6fe9e464..3609132a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -711,6 +711,8 @@ API](http://admin.wechat.com/wiki/index.php?title=Customer_Service_Messages). # API request data as defined by the WeChat API. [ message: | default = '{{ template "wechat.default.message" . }}' ] +# Type of the message type, supported values are `text` and `markdown`. +[ message_type: | default = 'text' ] [ agent_id: | default = '{{ template "wechat.default.agent_id" . }}' ] [ to_user: | default = '{{ template "wechat.default.to_user" . }}' ] [ to_party: | default = '{{ template "wechat.default.to_party" . }}' ] diff --git a/notify/wechat/wechat.go b/notify/wechat/wechat.go index 17c15603..5e2d7aab 100644 --- a/notify/wechat/wechat.go +++ b/notify/wechat/wechat.go @@ -51,13 +51,14 @@ type token struct { } type weChatMessage struct { - Text weChatMessageContent `yaml:"text,omitempty" json:"text,omitempty"` - ToUser string `yaml:"touser,omitempty" json:"touser,omitempty"` - ToParty string `yaml:"toparty,omitempty" json:"toparty,omitempty"` - Totag string `yaml:"totag,omitempty" json:"totag,omitempty"` - AgentID string `yaml:"agentid,omitempty" json:"agentid,omitempty"` - Safe string `yaml:"safe,omitempty" json:"safe,omitempty"` - Type string `yaml:"msgtype,omitempty" json:"msgtype,omitempty"` + Text weChatMessageContent `yaml:"text,omitempty" json:"text,omitempty"` + ToUser string `yaml:"touser,omitempty" json:"touser,omitempty"` + ToParty string `yaml:"toparty,omitempty" json:"toparty,omitempty"` + Totag string `yaml:"totag,omitempty" json:"totag,omitempty"` + AgentID string `yaml:"agentid,omitempty" json:"agentid,omitempty"` + Safe string `yaml:"safe,omitempty" json:"safe,omitempty"` + Type string `yaml:"msgtype,omitempty" json:"msgtype,omitempty"` + Markdown weChatMessageContent `yaml:"markdown,omitempty" json:"markdown,omitempty"` } type weChatMessageContent struct { @@ -135,16 +136,23 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) } msg := &weChatMessage{ - Text: weChatMessageContent{ - Content: tmpl(n.conf.Message), - }, ToUser: tmpl(n.conf.ToUser), ToParty: tmpl(n.conf.ToParty), Totag: tmpl(n.conf.ToTag), AgentID: tmpl(n.conf.AgentID), - Type: "text", + Type: n.conf.MessageType, Safe: "0", } + + if msg.Type == "markdown" { + msg.Markdown = weChatMessageContent{ + Content: tmpl(n.conf.Message), + } + } else { + msg.Text = weChatMessageContent{ + Content: tmpl(n.conf.Message), + } + } if err != nil { return false, fmt.Errorf("templating error: %s", err) } diff --git a/notify/wechat/wechat_test.go b/notify/wechat/wechat_test.go index 46085dd7..56cfa05c 100644 --- a/notify/wechat/wechat_test.go +++ b/notify/wechat/wechat_test.go @@ -67,3 +67,26 @@ func TestWechatRedactedURLOnNotify(t *testing.T) { test.AssertNotifyLeaksNoSecret(t, ctx, notifier, secret, token) } + +func TestWechatMessageTypeSelector(t *testing.T) { + secret, token := "secret", "token" + ctx, u, fn := test.GetContextWithCancelingURL(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, `{"access_token":"%s"}`, token) + }) + defer fn() + + notifier, err := New( + &config.WechatConfig{ + APIURL: &config.URL{URL: u}, + HTTPConfig: &commoncfg.HTTPClientConfig{}, + CorpID: "corpid", + APISecret: config.Secret(secret), + MessageType: "markdown", + }, + test.CreateTmpl(t), + log.NewNopLogger(), + ) + require.NoError(t, err) + + test.AssertNotifyLeaksNoSecret(t, ctx, notifier, secret, token) +}