Merge pull request #5357 from rainzm/automated-cherry-pick-of-#5353-upstream-release-3.1

Automated cherry pick of #5353: feature(notify): Add query and return value for contact UPDATE interface
This commit is contained in:
yunion-ci-robot
2020-03-05 19:03:51 +08:00
committed by GitHub
9 changed files with 88 additions and 55 deletions
+23 -12
View File
@@ -29,11 +29,11 @@ func init() {
* 操作用户的通信地址(如果用户的通信地址不存在则进行添加;如果已存在则进行修改;如果设置空则进行删除。)
*/
type ContactsUpdateOptions struct {
UID string `help:"The user you wanna add contact to (Keystone User ID)"`
CONTACTTYPE string `help:"The contact type email|mobile" choices:"email|mobile|dingtalk"`
CONTACT string `help:"The contacts details mobile number or email address or dingtalk's userid, if set it the empty str means delete"`
Status string `help:"Enabled or disabled contact status" choices:"enable|disable"`
UpdateDingtalk bool `help:"if update dingtalk"`
UID string `help:"The user you wanna add contact to (Keystone User ID)"`
CONTACTTYPE string `help:"The contact type email|mobile" choices:"email|mobile|dingtalk"`
CONTACT string `help:"The contacts details mobile number or email address or dingtalk's userid, if set it the empty str means delete"`
Status string `help:"Enabled or disabled contact status" choices:"enable|disable"`
Pull string `help:"pull some subcontacts(e.g., dingtalk, feishu, etc) related to mobile"`
}
R(&ContactsUpdateOptions{}, "contact-update", "Create, delete or update contact for user", func(s *mcclient.ClientSession, args *ContactsUpdateOptions) error {
arr := jsonutils.NewArray()
@@ -52,12 +52,8 @@ func init() {
params := jsonutils.NewDict()
params.Add(arr, "contacts")
if args.UpdateDingtalk {
params.Add(jsonutils.JSONTrue, "update_dingtalk")
}
contact, err := modules.Contacts.CustomizedPerformAction(s, args.UID, "update-contact", params)
contact, err := modules.Contacts.CustomizedPerformAction(s, args.UID, "update-contact", args.Pull, params)
if err != nil {
return err
}
@@ -66,6 +62,21 @@ func init() {
return nil
})
type ContactsPullOptions struct {
UID string `help:"The user you wanna pull contact"`
CONTACTTYPE string `help:"The contact type"`
}
R(&ContactsPullOptions{}, "contact-pull", "Pull contact", func(s *mcclient.ClientSession, args *ContactsPullOptions) error {
params := jsonutils.NewDict()
params.Set("contacts", jsonutils.NewArray())
contact, err := modules.Contacts.CustomizedPerformAction(s, args.UID, "update-contact", args.CONTACTTYPE, params)
if err != nil {
return err
}
printObject(contact)
return nil
})
type ContactsDeleteOptions struct {
UID string `help:"The user you wanna add contact to (Keystone User ID)"`
CONTACTTYPE string `help:"The contact type email|mobile|dingtalk" choices:"email|mobile|dingtalk"`
@@ -78,7 +89,7 @@ func init() {
arr.Add(tmpObj)
params := jsonutils.NewDict()
params.Add(arr, "contacts")
contact, err := modules.Contacts.CustomizedPerformAction(s, args.UID, "update-contact", params)
contact, err := modules.Contacts.CustomizedPerformAction(s, args.UID, "update-contact", "", params)
if err != nil {
return err
}
@@ -169,7 +180,7 @@ func init() {
tmpDict := jsonutils.NewDict()
tmpDict.Add(jsonutils.NewString(args.CONTACT_TYPE), "contact_type")
tmpDict.Add(jsonutils.NewString(args.CONTACT), "contact")
_, err := modules.Contacts.CustomizedPerformAction(s, args.UID, "verify", tmpDict)
_, err := modules.Contacts.CustomizedPerformAction(s, args.UID, "verify", "", tmpDict)
if err != nil {
return err
}
+1 -1
View File
@@ -29,7 +29,7 @@ func init() {
*/
type NotificationCreateOptions struct {
CONTACTTYPE string `help:"User's contacts type" choices:"email|mobile|dingtalk|webconsole"`
CONTACTTYPE string `help:"User's contacts type"`
TOPIC string `help:"Title or topic of the notification"`
PRIORITY string `help:"Priority of the notification" choices:"normal|important|fatal"`
MSG string `help:"The content of the notification"`
+9 -4
View File
@@ -115,10 +115,15 @@ func (manager *SUserCacheManager) FetchUserFromKeystone(ctx context.Context, idS
user, err := modules.UsersV3.GetById(s, idStr, nil)
if err != nil {
if je, ok := err.(*httputils.JSONClientError); ok && je.Code == 404 {
return nil, sql.ErrNoRows
user, err = modules.UsersV3.GetByName(s, idStr, nil)
if je, ok := err.(*httputils.JSONClientError); ok && je.Code == 404 {
return nil, sql.ErrNoRows
}
}
if err != nil {
log.Errorf("fetch user %s fail %s", idStr, err)
return nil, errors.Wrap(err, "modules.UsersV3.Get")
}
log.Errorf("fetch user %s fail %s", idStr, err)
return nil, errors.Wrap(err, "modules.UsersV3.Get")
}
id, _ := user.GetString("id")
name, _ := user.GetString("name")
@@ -133,7 +138,7 @@ func (manager *SUserCacheManager) Save(ctx context.Context, idStr string, name s
objo, err := manager.FetchById(idStr)
if err != nil && err != sql.ErrNoRows {
log.Errorf("FetchTenantbyId fail %s", err)
log.Errorf("FetchUserbyId fail %s", err)
return nil, err
}
if err == nil {
+5 -2
View File
@@ -45,7 +45,7 @@ func (this *ContactsManager) DoBatchDeleteContacts(s *mcclient.ClientSession, pa
return modulebase.Post(this.ResourceManager, s, path, params, this.Keyword)
}
func (this *ContactsManager) CustomizedPerformAction(session *mcclient.ClientSession, id string, action string,
func (this *ContactsManager) CustomizedPerformAction(session *mcclient.ClientSession, id, action, pull string,
params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
body := jsonutils.NewDict()
@@ -53,7 +53,10 @@ func (this *ContactsManager) CustomizedPerformAction(session *mcclient.ClientSes
body.Add(params, this.Keyword)
}
path := fmt.Sprintf("/%s/%s/%s?uname=true", this.ContextPath(nil), url.PathEscape(id), url.PathEscape(action))
return modulebase.Post(this.ResourceManager, session, path, params, this.KeywordPlural)
if len(pull) > 0 {
path += fmt.Sprintf("&pull=%s", pull)
}
return modulebase.Post(this.ResourceManager, session, path, body, this.KeywordPlural)
}
func (this *ContactsManager) CustomizedGet(session *mcclient.ClientSession, id string,
+22 -6
View File
@@ -379,7 +379,7 @@ func (self *NotifyModelDispatcher) DeleteContacts(ctx context.Context, uids2 []j
// UpdateContacts analysis the data and update corresponding contacts if they exist in the database create new ones.
func (self *NotifyModelDispatcher) UpdateContacts(ctx context.Context, idstr string, query jsonutils.JSONObject,
datas []jsonutils.JSONObject, ctxIds []dispatcher.SResourceContext) error {
datas []jsonutils.JSONObject, ctxIds []dispatcher.SResourceContext) (jsonutils.JSONObject, error) {
type pair struct {
contact string
@@ -406,7 +406,7 @@ func (self *NotifyModelDispatcher) UpdateContacts(ctx context.Context, idstr str
records, err := models.ContactManager.FetchByUIDAndCType(idstr, contactTypes)
if err != nil {
return httperrors.NewGeneralError(err)
return nil, httperrors.NewGeneralError(err)
}
// updateFailed record the information of failed update
@@ -482,13 +482,29 @@ func (self *NotifyModelDispatcher) UpdateContacts(ctx context.Context, idstr str
errInfoBuffer.WriteString(" create failed. ")
}
errInfo := errInfoBuffer.String()
return httperrors.NewGeneralError(errors.Error(errInfo))
return nil, httperrors.NewGeneralError(errors.Error(errInfo))
}
if query.Contains("update_dingtalk") {
models.UpdateDingtalk(idstr)
if query.Contains("pull") {
cType, _ := query.GetString("pull")
models.PullContact(idstr, cType)
}
return nil
// keep the return value same as this of the GET interface
ret := jsonutils.NewDict()
contact, err := models.ContactManager.FetchByUIDs(ctx, []string{idstr}, false)
if err != nil {
log.Errorf(err.Error())
return ret, nil
}
outDetails, err := contact[0].GetExtraDetails(ctx, userCred, ret, false)
if err != nil {
log.Errorf(err.Error())
return ret, nil
}
out := jsonutils.Marshal(outDetails)
out.(*jsonutils.JSONDict).Set("created_at", jsonutils.NewString(contact[0].CreatedAt.String()))
return out, nil
}
func (self *NotifyModelDispatcher) UpdateTemplate(ctx context.Context, ctype string, query jsonutils.JSONObject,
+2 -8
View File
@@ -306,19 +306,13 @@ func contactUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Re
}
uid := params["<uid>"]
queryDict := mergeQueryParams(params, query)
update, _ := body.Bool(manager.Keyword(), "update_dingtalk")
if update {
dict := queryDict.(*jsonutils.JSONDict)
dict.Add(jsonutils.JSONTrue, "update_dingtalk")
}
err = manager.UpdateContacts(ctx, uid, queryDict, data, nil)
out, err := manager.UpdateContacts(ctx, uid, mergeQueryParams(params, query), data, nil)
if err != nil {
log.Errorf(err.Error())
httperrors.BadRequestError(w, "")
return
}
return
appsrv.SendJSON(w, wrap(out, manager.Keyword()))
}
// delete contact handler
+2
View File
@@ -115,6 +115,8 @@ func (self *SContactManager) InitializeData() error {
return nil
}
// FetchByUIDs fetch all SContancts whose uid included in uids.
// If some elements of uids are uname of users, setting param 'uname' as true will fetch correct results.
func (self *SContactManager) FetchByUIDs(ctx context.Context, uids []string, uname bool) ([]SContact, error) {
var err error
if uname {
+17 -17
View File
@@ -108,60 +108,60 @@ func SendVerifyMessage(ctx context.Context, userCred mcclient.TokenCredential, v
return nil
}
func UpdateDingtalk(uid string) {
func PullContact(uid string, contactType string) {
workMan.Run(func() {
updateDingtalk(context.Background(), uid)
pullContact(context.Background(), uid, contactType)
}, nil, nil)
}
func updateDingtalk(ctx context.Context, uid string) {
contacts, err := ContactManager.FetchByUIDAndCType(uid, []string{MOBILE, DINGTALK})
func pullContact(ctx context.Context, uid string, contactType string) {
contacts, err := ContactManager.FetchByUIDAndCType(uid, []string{MOBILE, contactType})
if err != nil {
log.Errorf("fetch contacts error")
}
if len(contacts) == 0 {
return
}
var mobileContact, dingtalkContact *SContact
var mobileContact, subContact *SContact
for i := range contacts {
if contacts[i].ContactType == MOBILE {
mobileContact = &contacts[i]
} else {
dingtalkContact = &contacts[i]
subContact = &contacts[i]
}
}
if mobileContact == nil {
return
}
userid, err := NotifyService.ContactByMobile(ctx, mobileContact.Contact, DINGTALK)
userid, err := NotifyService.ContactByMobile(ctx, mobileContact.Contact, contactType)
if err != nil {
log.Errorf("fetch dingtalk userid by mobile failed: %s", err.Error())
log.Errorf("fetch %s contact by mobile failed: %s", contactType, err.Error())
}
if dingtalkContact != nil {
dingtalkContact.SetModelManager(ContactManager, dingtalkContact)
origin := dingtalkContact.Contact
_, err := db.Update(dingtalkContact, func() error {
dingtalkContact.Contact = userid
if subContact != nil {
subContact.SetModelManager(ContactManager, subContact)
origin := subContact.Contact
_, err := db.Update(subContact, func() error {
subContact.Contact = userid
return nil
})
if err != nil {
log.Errorf("update dingtalk userid %s => %s failed", origin, userid)
log.Errorf("update %s contact userid %s => %s failed", contactType, origin, userid)
}
return
}
contact := SContact{
UID: uid,
ContactType: DINGTALK,
ContactType: contactType,
Contact: userid,
Enabled: "1",
VerifiedAt: time.Now(),
}
contact.Status = CONTACT_VERIFIED
err = ContactManager.TableSpec().InsertOrUpdate(&contact)
err = ContactManager.TableSpec().Insert(&contact)
if err != nil {
log.Errorf("create new dingtalk contact failed")
log.Errorf("create new %s contact failed", contactType)
}
}
+7 -5
View File
@@ -82,7 +82,7 @@ func (self *SRpcService) InitAll() error {
}
}
if self.SendServices.Len() == 0 {
log.Errorf("No available send service.")
log.Infof("No available send service.")
} else {
log.Infof("Total %d send service init successful", self.SendServices.Len())
}
@@ -285,12 +285,14 @@ func (self *SRpcService) startNewService(ctx context.Context, serviceName string
_, err = sendService.UpdateConfig(ctx, &args)
if err != nil {
st := status.Convert(err)
if st.Code() == codes.Unavailable {
if st.Code() == codes.FailedPrecondition {
// no such rpc serve
os.Remove(filename)
return nil, fmt.Errorf("no such rpc serve")
err = fmt.Errorf(st.Message())
}
return nil, fmt.Errorf(st.Message())
if st.Code() == codes.Unavailable {
err = fmt.Errorf("service is unavailable for now: %s", st.Message())
}
return nil, errors.Wrap(err, "UpdateConfig")
}
return sendService, nil