subcontact_pull_task.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package tasks
  15. import (
  16. "context"
  17. "fmt"
  18. "strings"
  19. "yunion.io/x/jsonutils"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/tristate"
  23. "yunion.io/x/pkg/utils"
  24. "yunion.io/x/sqlchemy"
  25. apis "yunion.io/x/onecloud/pkg/apis/notify"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/db"
  27. "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
  28. "yunion.io/x/onecloud/pkg/mcclient/auth"
  29. "yunion.io/x/onecloud/pkg/mcclient/modules/identity"
  30. "yunion.io/x/onecloud/pkg/notify/models"
  31. "yunion.io/x/onecloud/pkg/util/logclient"
  32. )
  33. var PullContactType = []string{
  34. apis.DINGTALK,
  35. apis.FEISHU,
  36. apis.WORKWX,
  37. }
  38. var UserContactType = []string{
  39. apis.EMAIL,
  40. apis.MOBILE,
  41. }
  42. var allContactTypes = []string{
  43. apis.DINGTALK,
  44. apis.FEISHU,
  45. apis.WORKWX,
  46. apis.EMAIL,
  47. apis.MOBILE,
  48. }
  49. type SubcontactPullTask struct {
  50. taskman.STask
  51. }
  52. func init() {
  53. taskman.RegisterTask(SubcontactPullTask{})
  54. }
  55. func (self *SubcontactPullTask) taskFailed(ctx context.Context, receiver *models.SReceiver, reason string) {
  56. log.Errorf("fail to pull subcontact of receiver %q: %s", receiver.Id, reason)
  57. receiver.SetStatus(ctx, self.UserCred, apis.RECEIVER_STATUS_PULL_FAILED, reason)
  58. logclient.AddActionLogWithContext(ctx, receiver, logclient.ACT_PULL_SUBCONTACT, reason, self.UserCred, false)
  59. self.SetStageFailed(ctx, jsonutils.NewString(reason))
  60. }
  61. func (self *SubcontactPullTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
  62. failedReasons := make([]string, 0)
  63. // pull contacts
  64. receiver := obj.(*models.SReceiver)
  65. if len(receiver.Mobile) == 0 {
  66. self.SetStageComplete(ctx, nil)
  67. return
  68. }
  69. // sync email and mobile to keystone
  70. s := auth.GetSession(ctx, self.UserCred, "")
  71. mobile := receiver.Mobile
  72. if strings.HasPrefix(mobile, "+") {
  73. spaceIdx := strings.Index(mobile, " ")
  74. if spaceIdx > 0 {
  75. mobile = mobile[spaceIdx+1:]
  76. }
  77. mobile = strings.TrimSpace(mobile)
  78. }
  79. params := map[string]string{
  80. "email": receiver.Email,
  81. "mobile": receiver.Mobile,
  82. }
  83. _, err := identity.UsersV3.Update(s, receiver.Id, jsonutils.Marshal(params))
  84. if err != nil {
  85. log.Errorf("update user email and mobile fail %s", err)
  86. }
  87. var contactTypes []string
  88. if self.Params.Contains("contact_types") {
  89. jArray, _ := self.Params.Get("contact_types")
  90. contactTypes = jArray.(*jsonutils.JSONArray).GetStringArray()
  91. }
  92. // 遍历所有通知渠道
  93. for _, contactType := range allContactTypes {
  94. // 若该渠道在输入渠道内,则设为enable
  95. if utils.IsInStringArray(contactType, contactTypes) {
  96. // 常规渠道
  97. if utils.IsInStringArray(contactType, PullContactType) {
  98. content := ""
  99. driver := models.GetDriver(contactType)
  100. content, err = driver.ContactByMobile(ctx, mobile, self.UserCred.GetDomainId())
  101. if err != nil {
  102. var reason string
  103. if errors.Cause(err) == apis.ErrNoSuchMobile {
  104. receiver.MarkContactTypeUnVerified(ctx, contactType, apis.ErrNoSuchMobile.Error())
  105. reason = fmt.Sprintf("%q: no such mobile %s", contactType, receiver.Mobile)
  106. } else if errors.Cause(err) == apis.ErrIncompleteConfig {
  107. receiver.MarkContactTypeUnVerified(ctx, contactType, apis.ErrIncompleteConfig.Error())
  108. reason = fmt.Sprintf("%q: %v", contactType, err)
  109. } else {
  110. receiver.MarkContactTypeUnVerified(ctx, contactType, "service exceptions")
  111. reason = fmt.Sprintf("%q: %v", contactType, err)
  112. }
  113. failedReasons = append(failedReasons, reason)
  114. continue
  115. }
  116. subcontact := []models.SSubContact{}
  117. q := models.SubContactManager.Query()
  118. cond := sqlchemy.AND(sqlchemy.Equals(q.Field("receiver_id"), receiver.Id), sqlchemy.Equals(q.Field("type"), contactType))
  119. q.Filter(cond)
  120. err = db.FetchModelObjects(models.SubContactManager, q, &subcontact)
  121. if err != nil {
  122. failedReasons = append(failedReasons, err.Error())
  123. continue
  124. }
  125. subid := ""
  126. if len(subcontact) > 0 {
  127. subid = subcontact[0].Id
  128. }
  129. err = models.SubContactManager.TableSpec().InsertOrUpdate(ctx, &models.SSubContact{
  130. SStandaloneResourceBase: db.SStandaloneResourceBase{
  131. SStandaloneAnonResourceBase: db.SStandaloneAnonResourceBase{Id: subid},
  132. },
  133. ReceiverID: receiver.Id,
  134. Type: contactType,
  135. Contact: content,
  136. ParentContactType: "mobile",
  137. Enabled: tristate.True,
  138. })
  139. if err != nil {
  140. failedReasons = append(failedReasons, err.Error())
  141. continue
  142. }
  143. receiver.SetContact(contactType, content)
  144. receiver.MarkContactTypeVerified(ctx, contactType)
  145. } else {
  146. _, err := db.Update(receiver, func() error {
  147. if contactType == apis.MOBILE {
  148. receiver.EnabledMobile = tristate.True
  149. }
  150. if contactType == apis.EMAIL {
  151. receiver.EnabledEmail = tristate.True
  152. }
  153. return nil
  154. })
  155. if err != nil {
  156. failedReasons = append(failedReasons, err.Error())
  157. continue
  158. }
  159. }
  160. } else {
  161. // 若该渠道在输入渠道内,则设为disable
  162. if utils.IsInStringArray(contactType, PullContactType) {
  163. subcontact := []models.SSubContact{}
  164. q := models.SubContactManager.Query()
  165. cond := sqlchemy.AND(sqlchemy.Equals(q.Field("receiver_id"), receiver.Id), sqlchemy.Equals(q.Field("type"), contactType))
  166. q.Filter(cond)
  167. err = db.FetchModelObjects(models.SubContactManager, q, &subcontact)
  168. if err != nil {
  169. failedReasons = append(failedReasons, err.Error())
  170. continue
  171. }
  172. subid := ""
  173. if len(subcontact) > 0 {
  174. subid = subcontact[0].Id
  175. }
  176. err = models.SubContactManager.TableSpec().InsertOrUpdate(ctx, &models.SSubContact{
  177. SStandaloneResourceBase: db.SStandaloneResourceBase{
  178. SStandaloneAnonResourceBase: db.SStandaloneAnonResourceBase{Id: subid},
  179. },
  180. ReceiverID: receiver.Id,
  181. Type: contactType,
  182. ParentContactType: "mobile",
  183. Enabled: tristate.False,
  184. })
  185. if err != nil {
  186. failedReasons = append(failedReasons, err.Error())
  187. continue
  188. }
  189. } else {
  190. _, err := db.Update(receiver, func() error {
  191. if contactType == apis.MOBILE {
  192. receiver.EnabledMobile = tristate.False
  193. }
  194. if contactType == apis.EMAIL {
  195. receiver.EnabledEmail = tristate.False
  196. }
  197. return nil
  198. })
  199. if err != nil {
  200. failedReasons = append(failedReasons, err.Error())
  201. continue
  202. }
  203. }
  204. }
  205. }
  206. if len(failedReasons) > 0 {
  207. reason := strings.Join(failedReasons, "; ")
  208. self.taskFailed(ctx, receiver, reason)
  209. return
  210. }
  211. // success
  212. receiver.SetStatus(ctx, self.UserCred, apis.RECEIVER_STATUS_READY, "")
  213. logclient.AddActionLogWithContext(ctx, receiver, logclient.ACT_PULL_SUBCONTACT, "", self.UserCred, true)
  214. self.SetStageComplete(ctx, nil)
  215. }