Skip to content

feature: eventbox support custom notify channel #2460

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions apistructs/notify_channel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) 2021 Terminus, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apistructs

// NotifyChannelFetchResponse 通知渠道详情响应结构
type NotifyChannelFetchResponse struct {
Header
Data NotifyChannelDTO `json:"data"`
}

// NotifyChannelDTO 通知渠道结构
type NotifyChannelDTO struct {
ID string `json:"id"`
Name string `json:"name"`
Type struct {
Name NotifyChannelType `json:"name"`
DisplayName string `json:"displayName"`
} `json:"type"`
Config *NotifyChannelConfig `json:"config"`
ScopeId string `json:"scopeId"`
ScopeType string `json:"scopeType"`
ChannelProviderType struct {
Name NotifyChannelProviderType `json:"name"`
DisplayName string `json:"displayName"`
} `json:"channelProviderType"`
Enable bool `json:"enable"`
}

type NotifyChannelConfig struct {
AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"`
SignName string `json:"signName"`
TemplateCode string `json:"templateCode"`
}

type NotifyChannelType string
type NotifyChannelProviderType string

const NOTIFY_CHANNEL_TYPE_SHORT_MESSAGE = NotifyChannelType("short_message")
const NOTIFY_CHANNEL_PROVIDER_TYPE_ALIYUN = NotifyChannelProviderType("aliyun_sms")
90 changes: 90 additions & 0 deletions apistructs/notify_channel_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright (c) 2021 Terminus, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apistructs

import (
"encoding/json"
"testing"
)

func Test_NotifyChannelDTO_Deserialization(t *testing.T) {
var jsonStr = `{
"id": "85fe1f9e-7cc4-4d65-baab-be0346d53849",
"name": "test_channel18",
"type": {
"name": "short_message",
"displayName": "短信"
},
"config": {
"accessKeyId": "xx",
"accessKeySecret": "xx",
"signName": "xx",
"templateCode": "xx"
},
"scopeId": "1",
"scopeType": "org",
"creatorName": "admin",
"createAt": "2021-10-20 21:10:14",
"updateAt": "2021-10-20 21:30:02",
"channelProviderType": {
"name": "aliyun_sms",
"displayName": "阿里云短信服务"
},
"enable": true
}`
var want = NotifyChannelDTO{
ID: "85fe1f9e-7cc4-4d65-baab-be0346d53849",
Name: "test_channel18",
Type: struct {
Name NotifyChannelType `json:"name"`
DisplayName string `json:"displayName"`
}{Name: NOTIFY_CHANNEL_TYPE_SHORT_MESSAGE, DisplayName: "短信"},
Config: &NotifyChannelConfig{
AccessKeyId: "xx",
AccessKeySecret: "xx",
SignName: "xx",
TemplateCode: "xx",
},
ScopeId: "1",
ScopeType: "org",
ChannelProviderType: struct {
Name NotifyChannelProviderType `json:"name"`
DisplayName string `json:"displayName"`
}{Name: NOTIFY_CHANNEL_PROVIDER_TYPE_ALIYUN, DisplayName: "阿里云短信服务"},
Enable: true,
}

var data NotifyChannelDTO

err := json.Unmarshal([]byte(jsonStr), &data)
if err != nil {
t.Errorf("should not error: %s", err)
}

if data.ID != want.ID || data.Enable != want.Enable || data.ScopeId != want.ScopeId {
t.Errorf("assert id,enable,scopeId property failed")
}
if data.Config == nil {
t.Errorf("config should not nil")
}

if data.Type.Name != want.Type.Name {
t.Errorf("channel type assert failed, want: %s, actual: %s", want.Type.Name, data.Type.Name)
}

if data.ChannelProviderType.Name != want.ChannelProviderType.Name {
t.Errorf("channel provier type assert failed, want: %s, actual: %s", want.ChannelProviderType.Name, data.ChannelProviderType.Name)
}
}
44 changes: 44 additions & 0 deletions bundle/notify_channel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2021 Terminus, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bundle

import (
"fmt"

"github.com/erda-project/erda/apistructs"
"github.com/erda-project/erda/bundle/apierrors"
"github.com/erda-project/erda/pkg/http/httputil"
)

// GetEnabledNotifyChannelByType get enabled notify channel of specific from core-service.
func (b *Bundle) GetEnabledNotifyChannelByType(orgID interface{}, channelType apistructs.NotifyChannelType) (*apistructs.NotifyChannelDTO, error) {
host, err := b.urls.CoreServices()
if err != nil {
return nil, err
}
hc := b.hc

var fetchResp apistructs.NotifyChannelFetchResponse
resp, err := hc.Get(host).Path(fmt.Sprintf("/api/notify-channel/enabled?scopeType=org&scopeId=%v&type=%v", orgID, channelType)).
Header(httputil.InternalHeader, "bundle").Do().JSON(&fetchResp)
if err != nil {
return nil, apierrors.ErrInvoke.InternalError(err)
}
if !resp.IsOK() || !fetchResp.Success {
return nil, toAPIError(resp.StatusCode(), fetchResp.Error)
}

return &fetchResp.Data, nil
}
3 changes: 3 additions & 0 deletions modules/eventbox/subscriber/group/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ func (d *GroupSubscriber) Publish(dest string, content string, time int64, msg *
}
// 监控的sms,vms需要
channel.Params["message"] = groupNotifyContent.NotifyItemDisplayName
if _, ok = channel.Params["content"]; !ok {
channel.Params["content"] = channel.Template
}
request := map[string]interface{}{
"template": channel.Template,
"type": channel.Type,
Expand Down
18 changes: 18 additions & 0 deletions modules/eventbox/subscriber/sms/sms.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/sirupsen/logrus"

"github.com/erda-project/erda/apistructs"
"github.com/erda-project/erda/bundle"
"github.com/erda-project/erda/modules/eventbox/subscriber"
"github.com/erda-project/erda/modules/eventbox/types"
Expand Down Expand Up @@ -76,12 +77,26 @@ func (d *MobileSubscriber) Publish(dest string, content string, time int64, msg
logrus.Errorf("failed to get org info err:%s", err)
}

notifyChannel, err := d.bundle.GetEnabledNotifyChannelByType(mobileData.OrgID, apistructs.NOTIFY_CHANNEL_TYPE_SHORT_MESSAGE)
if err != nil {
logrus.Errorf("failed to get notifychannel, err: %s", err)
}

if notifyChannel.ChannelProviderType.Name != "" && notifyChannel.ChannelProviderType.Name != apistructs.NOTIFY_CHANNEL_PROVIDER_TYPE_ALIYUN {
logrus.Errorf("do not support channel provider: %s", notifyChannel.ChannelProviderType.Name)
}

accessKeyID, accessSecret, signName := d.accessKeyId, d.accessSecret, d.signName
if err == nil && org.Config != nil && org.Config.SMSKeyID != "" && org.Config.SMSKeySecret != "" {
accessKeyID = org.Config.SMSKeyID
accessSecret = org.Config.SMSKeySecret
signName = org.Config.SMSSignName
}
if err == nil && notifyChannel.Config != nil && notifyChannel.Config.AccessKeyId != "" && notifyChannel.Config.AccessKeySecret != "" {
accessKeyID = notifyChannel.Config.AccessKeyId
accessSecret = notifyChannel.Config.AccessKeySecret
signName = notifyChannel.Config.SignName
}

sdkClient, err := sdk.NewClientWithAccessKey("cn-hangzhou", accessKeyID, accessSecret)
if err != nil {
Expand All @@ -96,6 +111,9 @@ func (d *MobileSubscriber) Publish(dest string, content string, time int64, msg
if err == nil && org.Config != nil && org.Config.SMSMonitorTemplateCode != "" {
templateCode = org.Config.SMSMonitorTemplateCode
}
if err == nil && notifyChannel.Config != nil && notifyChannel.Config.TemplateCode != "" {
templateCode = notifyChannel.Config.TemplateCode
}
}

if templateCode == "" {
Expand Down
14 changes: 11 additions & 3 deletions modules/pkg/etcdclient/etcdclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ func NewEtcdClient() (*clientv3.Client, error) {
}
if url.Scheme == "https" {
tlsInfo := transport.TLSInfo{
CertFile: "/certs/etcd-client.pem",
KeyFile: "/certs/etcd-client-key.pem",
TrustedCAFile: "/certs/etcd-ca.pem",
CertFile: getEnvOrDefault("ETCD_CERT_FILE", "/certs/etcd-client.pem"),
KeyFile: getEnvOrDefault("ETCD_CERT_KEY_FILE", "/certs/etcd-client-key.pem"),
TrustedCAFile: getEnvOrDefault("ETCD_CA_FILE", "/certs/etcd-ca.pem"),
}
tlsConfig, err = tlsInfo.ClientConfig()
if err != nil {
Expand All @@ -93,3 +93,11 @@ func NewEtcdClient() (*clientv3.Client, error) {
})
return cli, err
}

func getEnvOrDefault(key, defaultVal string) string {
if val := os.Getenv(key); val != "" {
return val
}

return defaultVal
}
46 changes: 46 additions & 0 deletions modules/pkg/etcdclient/etcdclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@

package etcdclient

import (
"os"
"testing"

"bou.ke/monkey"
)

//import (
// "sync"
// "testing"
Expand Down Expand Up @@ -44,3 +51,42 @@ package etcdclient
// }
//
//}

func Test_getEnvOrDefault(t *testing.T) {
nonExistsKey := "none_exists_key"
defaultValue := "default_value"
existsKey := "exists_key"
existsValue := "exists_value"

defer monkey.Unpatch(os.Getenv)
monkey.Patch(os.Getenv, func(key string) string {
if key == nonExistsKey {
return ""
}

return existsValue
})

val := getEnvOrDefault(nonExistsKey, defaultValue)
if val != defaultValue {
t.Errorf("with %s should return %s, but got: %s", nonExistsKey, defaultValue, val)
}

val = getEnvOrDefault(existsKey, defaultValue)
if val != existsValue {
t.Errorf("with %s should return %s, but got: %s", existsKey, existsValue, val)
}
}

func Test_NewEtcdClient_ReadCertFromEnv(t *testing.T) {

defer monkey.Unpatch(os.Getenv)
monkey.Patch(os.Getenv, func(key string) string {
return "mock"
})

_, err := NewEtcdClient()
if err != nil {
t.Errorf("should not throw error")
}
}