mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-10-30 18:23:35 +08:00
Merge pull request #9076 from rainzm/notify/spec_receiver
Notify via contact skip receiver
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user