notify: add markdown support for wechat (#2309)

* notify: add markdown support for wechat

Signed-off-by: Jason Cooper <master@deamwork.com>

* docs: update WeChat receiver configuration document

Signed-off-by: Jason Cooper <master@deamwork.com>

* fix: check WeChat msgType, apply default if not present

Signed-off-by: Jason Cooper <master@deamwork.com>

* chore: remove unnecessary comment

Signed-off-by: Jason Cooper <master@deamwork.com>

* fix: simplify msgType process

Signed-off-by: Jason Cooper <master@deamwork.com>

* docs: wechat configs document update

Signed-off-by: Jason Cooper <master@deamwork.com>

* fix: apply error message suggestions

Signed-off-by: Jason Cooper <master@deamwork.com>

* test: add test for regex

Signed-off-by: Jason Cooper <master@deamwork.com>

* fix: wechat message safe param

Signed-off-by: Jason Cooper <master@deamwork.com>
This commit is contained in:
Jason Cooper 2020-07-06 21:56:42 +08:00 committed by GitHub
parent 1895fde856
commit 277c9ed462
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 85 additions and 20 deletions

View File

@ -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.

View File

@ -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
}

View File

@ -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: <tmpl_string> | default = '{{ template "wechat.default.message" . }}' ]
# Type of the message type, supported values are `text` and `markdown`.
[ message_type: <string> | default = 'text' ]
[ agent_id: <string> | default = '{{ template "wechat.default.agent_id" . }}' ]
[ to_user: <string> | default = '{{ template "wechat.default.to_user" . }}' ]
[ to_party: <string> | default = '{{ template "wechat.default.to_party" . }}' ]

View File

@ -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)
}

View File

@ -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)
}