Merge pull request #9076 from rainzm/notify/spec_receiver

Notify via contact skip receiver
This commit is contained in:
Zexi Li
2020-11-30 21:50:06 +08:00
committed by GitHub
5 changed files with 170 additions and 64 deletions
+104 -38
View File
@@ -149,6 +149,16 @@ func Notify(recipientId []string, isGroup bool, priority npk.TNotifyPriority, ev
notify(context.Background(), recipientId, isGroup, priority, event, data)
}
func NotifyWithContact(ctx context.Context, contacts []string, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) {
p := sNotifyParams{
contacts: contacts,
priority: priority,
event: event,
data: data,
}
rawNotify(ctx, p)
}
func notify(ctx context.Context, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) {
switch priority {
case npk.NotifyPriorityCritical:
@@ -161,40 +171,63 @@ func notify(ctx context.Context, recipientId []string, isGroup bool, priority np
}
func RawNotifyWithCtx(ctx context.Context, recipientId []string, isGroup bool, channel npk.TNotifyChannel, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) {
rawNotify(ctx, recipientId, isGroup, channel, priority, event, data)
rawNotify(ctx, sNotifyParams{
recipientId: recipientId,
isGroup: isGroup,
channel: channel,
priority: priority,
event: event,
data: data,
})
}
func RawNotify(recipientId []string, isGroup bool, channel npk.TNotifyChannel, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) {
rawNotify(context.Background(), recipientId, isGroup, channel, priority, event, data)
rawNotify(context.Background(), sNotifyParams{
recipientId: recipientId,
isGroup: isGroup,
channel: channel,
priority: priority,
event: event,
data: data,
})
}
// IntelliNotify try to create receiver nonexistent if createReceiver is set to true
func IntelliNotify(ctx context.Context, recipientId []string, isGroup bool, channel npk.TNotifyChannel, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject, createReceiver bool) {
intelliNotify(ctx, recipientId, isGroup, channel, priority, event, data, createReceiver)
intelliNotify(ctx, sNotifyParams{
recipientId: recipientId,
isGroup: isGroup,
channel: channel,
priority: priority,
event: event,
data: data,
createReceiver: createReceiver,
})
}
const noSuchReceiver = `no such receiver whose uid is '(.*)'`
var noSuchReceiverRegexp = regexp.MustCompile(noSuchReceiver)
func intelliNotify(ctx context.Context, recipientId []string, isGroup bool, channel npk.TNotifyChannel, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject, createReceiver bool) {
log.Infof("notify %s event %s priority %s", recipientId, event, priority)
func intelliNotify(ctx context.Context, p sNotifyParams) {
log.Infof("notify %s event %s priority %s", p.recipientId, p.event, p.priority)
msg := npk.SNotifyMessage{}
if isGroup {
msg.Gid = recipientId
if p.isGroup {
msg.Gid = p.recipientId
} else {
msg.Uid = recipientId
msg.Uid = p.recipientId
}
msg.Priority = priority
msg.ContactType = channel
topic, _ := getContent(ctx, event, "title", channel, data)
msg.Priority = p.priority
msg.Contacts = p.contacts
msg.ContactType = p.channel
topic, _ := getContent(ctx, p.event, "title", p.channel, p.data)
if len(topic) == 0 {
topic = event
topic = p.event
}
msg.Topic = topic
body, _ := getContent(ctx, event, "content", channel, data)
body, _ := getContent(ctx, p.event, "content", p.channel, p.data)
if len(body) == 0 {
body, _ = data.GetString()
body, _ = p.data.GetString()
}
msg.Msg = body
// log.Debugf("send notification %s %s", topic, body)
@@ -205,7 +238,7 @@ func intelliNotify(ctx context.Context, recipientId []string, isGroup bool, chan
if err == nil {
break
}
if !createReceiver {
if !p.createReceiver {
log.Errorf("unable to send notification: %v", err)
break
}
@@ -236,8 +269,19 @@ func intelliNotify(ctx context.Context, recipientId []string, isGroup bool, chan
}, nil, nil)
}
func rawNotify(ctx context.Context, recipientId []string, isGroup bool, channel npk.TNotifyChannel, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) {
intelliNotify(ctx, recipientId, isGroup, channel, priority, event, data, false)
type sNotifyParams struct {
recipientId []string
isGroup bool
contacts []string
channel npk.TNotifyChannel
priority npk.TNotifyPriority
event string
data jsonutils.JSONObject
createReceiver bool
}
func rawNotify(ctx context.Context, p sNotifyParams) {
intelliNotify(ctx, p)
}
func NotifyNormal(recipientId []string, isGroup bool, event string, data jsonutils.JSONObject) {
@@ -249,18 +293,36 @@ func NotifyNormalWithCtx(ctx context.Context, recipientId []string, isGroup bool
}
func notifyNormal(ctx context.Context, recipientId []string, isGroup bool, event string, data jsonutils.JSONObject) {
for _, c := range []npk.TNotifyChannel{
p := sNotifyParams{
recipientId: recipientId,
isGroup: isGroup,
event: event,
data: data,
priority: npk.NotifyPriorityNormal,
}
notifyWithChannel(ctx, p,
npk.NotifyByEmail,
npk.NotifyByDingTalk,
npk.NotifyByWebConsole,
npk.NotifyByFeishu,
npk.NotifyByWorkwx,
} {
rawNotify(ctx, recipientId, isGroup,
c,
npk.NotifyPriorityNormal,
event, data)
npk.NotifyByWebConsole,
)
}
func notifyWithChannel(ctx context.Context, p sNotifyParams, channels ...npk.TNotifyChannel) {
reps := p.recipientId
for _, c := range channels {
p.recipientId = []string{}
p.contacts = []string{}
p.channel = c
if c == npk.NotifyByWebConsole {
p.contacts = reps
} else {
p.recipientId = reps
}
rawNotify(ctx, p)
}
}
func NotifyImportant(recipientId []string, isGroup bool, event string, data jsonutils.JSONObject) {
@@ -272,19 +334,21 @@ func NotifyImportantWithCtx(ctx context.Context, recipientId []string, isGroup b
}
func notifyImportant(ctx context.Context, recipientId []string, isGroup bool, event string, data jsonutils.JSONObject) {
for _, c := range []npk.TNotifyChannel{
p := sNotifyParams{
recipientId: recipientId,
isGroup: isGroup,
event: event,
data: data,
priority: npk.NotifyPriorityNormal,
}
notifyWithChannel(ctx, p,
npk.NotifyByEmail,
npk.NotifyByDingTalk,
npk.NotifyByMobile,
npk.NotifyByWebConsole,
npk.NotifyByFeishu,
npk.NotifyByWorkwx,
} {
rawNotify(ctx, recipientId, isGroup,
c,
npk.NotifyPriorityImportant,
event, data)
}
)
}
func NotifyCritical(recipientId []string, isGroup bool, event string, data jsonutils.JSONObject) {
@@ -296,19 +360,21 @@ func NotifyCriticalWithCtx(ctx context.Context, recipientId []string, isGroup bo
}
func notifyCritical(ctx context.Context, recipientId []string, isGroup bool, event string, data jsonutils.JSONObject) {
for _, c := range []npk.TNotifyChannel{
p := sNotifyParams{
recipientId: recipientId,
isGroup: isGroup,
event: event,
data: data,
priority: npk.NotifyPriorityNormal,
}
notifyWithChannel(ctx, p,
npk.NotifyByEmail,
npk.NotifyByDingTalk,
npk.NotifyByMobile,
npk.NotifyByWebConsole,
npk.NotifyByFeishu,
npk.NotifyByWorkwx,
} {
rawNotify(ctx, recipientId, isGroup,
c,
npk.NotifyPriorityCritical,
event, data)
}
)
}
// NotifyAllWithoutRobot will send messages via all contacnt type from exclude robot contact type such as dingtalk-robot.
@@ -32,6 +32,7 @@ type SNotifyMessage struct {
Uid []string `json:"uid,omitempty"`
Gid []string `json:"gid,omitempty"`
ContactType TNotifyChannel `json:"contact_type,omitempty"`
Contacts []string `json:"contracts"`
Topic string `json:"topic,omitempty"`
Priority TNotifyPriority `json:"priority,omitempty"`
Msg string `json:"msg,omitempty"`
@@ -41,6 +42,7 @@ type SNotifyMessage struct {
type SNotifyV2Message struct {
Receivers []string `json:"receivers"`
Contacts []string `json:"contacts"`
ContactType string `json:"contact_type"`
Topic string `json:"topic"`
Priority string `json:"priority"`
@@ -74,6 +76,7 @@ func (manager *NotificationManager) Send(s *mcclient.ClientSession, msg SNotifyM
v2msg := SNotifyV2Message{
Receivers: receiverIds,
Contacts: msg.Contacts,
ContactType: string(msg.ContactType),
Topic: msg.Topic,
Priority: string(msg.Priority),
+27 -12
View File
@@ -31,6 +31,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/image/policy"
"yunion.io/x/onecloud/pkg/mcclient"
notifyv2 "yunion.io/x/onecloud/pkg/notify"
"yunion.io/x/onecloud/pkg/notify/oldmodels"
@@ -71,24 +72,32 @@ type SNotification struct {
SendTimes int
}
const (
SendByContact = "send_by_contact"
)
func (nm *SNotificationManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.NotificationCreateInput) (api.NotificationCreateInput, error) {
// compatible
if len(input.Receivers) == 0 && input.ContactType == api.WEBCONSOLE {
input.Contacts = input.Receivers
input.Receivers = []string{}
}
if len(input.Receivers) == 0 {
if !userCred.IsAllow(rbacutils.ScopeSystem, api.SERVICE_TYPE, nm.KeywordPlural(), policy.PolicyActionPerform, SendByContact) {
return input, httperrors.NewForbiddenError("can't send notification by contact, need receiver")
}
if len(input.Contacts) == 0 {
input.Contacts = []string{""}
}
}
// check contact type enabled
allContactType, err := ConfigManager.allContactType()
if err != nil {
return input, err
}
switch {
case input.ContactType == api.WEBHOOK:
case utils.IsInStringArray(input.ContactType, ConfigManager.filterContactType(allContactType, "")):
//check uids, rids and contacts
if len(input.Receivers) == 0 && len(input.Contacts) == 0 {
return input, httperrors.NewMissingParameterError("receivers | contacts")
}
case utils.IsInStringArray(input.ContactType, ConfigManager.filterContactType(allContactType, api.CTYPE_ROBOT_ONLY)):
default:
return input, httperrors.NewInputParameterError("Unconfigured contact type %q", input.ContactType)
}
if !utils.IsInStringArray(input.ContactType, allContactType) {
return input, httperrors.NewInputParameterError("Unconfigured contact type %q", input.ContactType)
}
// check receivers
if len(input.Receivers) > 0 {
@@ -137,6 +146,12 @@ func (n *SNotification) CustomizeCreate(ctx context.Context, userCred mcclient.T
return errors.Wrap(err, "ReceiverNotificationManager.Create")
}
}
for i := range input.Contacts {
_, err := ReceiverNotificationManager.CreateWithoutReceiver(ctx, userCred, input.Contacts[i], n.Id)
if err != nil {
return errors.Wrap(err, "ReceiverNotificationManager.Create")
}
}
return nil
}
@@ -200,7 +215,7 @@ func (n *SNotification) ReceiveDetails() ([]api.ReceiveDetail, error) {
subRQ := ReceiverManager.Query("id", "name").SubQuery()
q := ReceiverNotificationManager.Query("receiver_id", "notification_id", "contact", "send_at", "send_by", "status", "failed_reason").Equals("notification_id", n.Id)
q.AppendField(subRQ.Field("name", "receiver_name"))
q = q.Join(subRQ, sqlchemy.Equals(q.Field("receiver_id"), subRQ.Field("id")))
q = q.LeftJoin(subRQ, sqlchemy.OR(sqlchemy.Equals(q.Field("receiver_id"), subRQ.Field("id")), sqlchemy.Equals(q.Field("contact"), subRQ.Field("id"))))
ret := make([]api.ReceiveDetail, 0, 2)
err := q.All(&ret)
if err != nil && errors.Cause(err) != sql.ErrNoRows {
+6 -1
View File
@@ -31,13 +31,17 @@ type SReceiverNotificationManager struct {
db.SJointResourceBaseManager
}
const (
ReceiverIdDefault = "default"
)
// +onecloud:swagger-gen-ignore
type SReceiverNotification struct {
db.SJointResourceBase
ReceiverID string `width:"128" charset:"ascii" nullable:"false"`
NotificationID string `width:"128" charset:"ascii" nullable:"false"`
// ignore if ReceiverID is not empty
// ignore if ReceiverID is not empty or default
Contact string `width:"128" nullable:"false"`
SendAt time.Time `nullable:"false"`
SendBy string `width:"128" nullable:"false"`
@@ -66,6 +70,7 @@ func (rnm *SReceiverNotificationManager) GetSlaveFieldName() string {
func (rnm *SReceiverNotificationManager) CreateWithoutReceiver(ctx context.Context, userCred mcclient.TokenCredential, contact, notificationID string) (*SReceiverNotification, error) {
rn := &SReceiverNotification{
NotificationID: notificationID,
ReceiverID: ReceiverIdDefault,
Contact: contact,
Status: api.RECEIVER_NOTIFICATION_RECEIVED,
SendBy: userCred.GetUserId(),
+30 -13
View File
@@ -49,7 +49,20 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
}
notification.SetStatus(self.UserCred, apis.NOTIFICATION_STATUS_SENDING, "")
// sort out what needs to be sent
// split rns
var (
rnsWithReceiver []*models.SReceiverNotification
rnsWithoutReceiver []*models.SReceiverNotification
)
for i := range rns {
if rns[i].ReceiverID == models.ReceiverIdDefault {
rnsWithoutReceiver = append(rnsWithoutReceiver, &rns[i])
} else {
rnsWithReceiver = append(rnsWithReceiver, &rns[i])
}
}
failedRecord := make([]string, 0)
sendFail := func(rn *models.SReceiverNotification, reason string) {
rn.AfterSend(ctx, false, reason)
@@ -58,50 +71,54 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
// build contactMap
contactMap := make(map[string]*models.SReceiverNotification)
for i := range rns {
if len(rns[i].ReceiverID) == 0 {
contactMap[rns[i].Contact] = &rns[i]
for i := range rnsWithReceiver {
if len(rnsWithReceiver[i].ReceiverID) == 0 {
contactMap[rnsWithReceiver[i].Contact] = rnsWithReceiver[i]
continue
}
receiver, err := rns[i].Receiver()
receiver, err := rnsWithReceiver[i].Receiver()
if err != nil {
sendFail(&rns[i], fmt.Sprintf("fail to fetch Receiver: %s", err.Error()))
sendFail(rnsWithReceiver[i], fmt.Sprintf("fail to fetch Receiver: %s", err.Error()))
continue
}
// check receiver enabled
if receiver.Enabled.IsFalse() {
sendFail(&rns[i], fmt.Sprintf("disabled receiver"))
sendFail(rnsWithReceiver[i], fmt.Sprintf("disabled receiver"))
continue
}
// check contact enabled
enabled, err := receiver.IsEnabledContactType(notification.ContactType)
if err != nil {
sendFail(&rns[i], fmt.Sprintf("IsEnabledContactType error for receiver: %s", err.Error()))
sendFail(rnsWithReceiver[i], fmt.Sprintf("IsEnabledContactType error for receiver: %s", err.Error()))
continue
}
if !enabled {
sendFail(&rns[i], fmt.Sprintf("disabled contactType %q", notification.ContactType))
sendFail(rnsWithReceiver[i], fmt.Sprintf("disabled contactType %q", notification.ContactType))
continue
}
// check contact verified
verified, err := receiver.IsVerifiedContactType(notification.ContactType)
if err != nil {
sendFail(&rns[i], fmt.Sprintf("IsVerifiedContactType error for receiver: %s", err.Error()))
sendFail(rnsWithReceiver[i], fmt.Sprintf("IsVerifiedContactType error for receiver: %s", err.Error()))
continue
}
if !verified {
sendFail(&rns[i], fmt.Sprintf("unverified contactType %q", notification.ContactType))
sendFail(rnsWithReceiver[i], fmt.Sprintf("unverified contactType %q", notification.ContactType))
continue
}
contact, err := receiver.GetContact(notification.ContactType)
if err != nil {
reason := fmt.Sprintf("fail to fetch contact: %s", err.Error())
sendFail(&rns[i], reason)
sendFail(rnsWithReceiver[i], reason)
continue
}
contactMap[contact] = &rns[i]
contactMap[contact] = rnsWithReceiver[i]
}
for i := range rnsWithoutReceiver {
contactMap[rnsWithoutReceiver[i].Contact] = rnsWithoutReceiver[i]
}
// set status before send