diff --git a/build/notify/root/opt/yunion/share/template/content/VERIFY.email b/build/notify/root/opt/yunion/share/template/content/VERIFY.email
index 612bd83941..042ae56740 100644
--- a/build/notify/root/opt/yunion/share/template/content/VERIFY.email
+++ b/build/notify/root/opt/yunion/share/template/content/VERIFY.email
@@ -47,16 +47,13 @@
- | 请点击下面的链接,完成用户邮箱的激活: |
-
-
- | {{.link}} |
+ 您正在验证邮箱,请在验证码输入框中输入:{{.code}},已完成验证。 |
- | *为了确保您的帐号安全,该链接仅48小时内访问有效,请勿直接回复此邮件。 |
+ 如非本人操作,请及时登录平台并修改密码以保证账户的安全 |
| 版权所有 © {{.copyright}} 保留一切权利 |
diff --git a/cmd/climc/main.go b/cmd/climc/main.go
index 6711336e78..89226423b8 100644
--- a/cmd/climc/main.go
+++ b/cmd/climc/main.go
@@ -32,7 +32,7 @@ import (
_ "yunion.io/x/onecloud/cmd/climc/shell/meter"
_ "yunion.io/x/onecloud/cmd/climc/shell/misc"
_ "yunion.io/x/onecloud/cmd/climc/shell/monitor"
- _ "yunion.io/x/onecloud/cmd/climc/shell/notify"
+ _ "yunion.io/x/onecloud/cmd/climc/shell/notifyv2"
_ "yunion.io/x/onecloud/cmd/climc/shell/servicetree"
_ "yunion.io/x/onecloud/cmd/climc/shell/yunionconf"
)
diff --git a/cmd/climc/shell/notify/contactgroups.go b/cmd/climc/shell/notify/contactgroups.go
deleted file mode 100644
index bd057befd8..0000000000
--- a/cmd/climc/shell/notify/contactgroups.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import (
- "yunion.io/x/jsonutils"
-
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/modules"
- "yunion.io/x/onecloud/pkg/mcclient/options"
-)
-
-func init() {
-
- /**
- * 获得一个全部通信地址组
- */
- type ContactGroupsListOptions struct {
- options.BaseListOptions
- }
- R(&ContactGroupsListOptions{}, "contact-group-list", "List all contact groups for all the domainsconta", func(s *mcclient.ClientSession, args *ContactGroupsListOptions) error {
- var params *jsonutils.JSONDict
- {
- var err error
- params, err = args.BaseListOptions.Params()
- if err != nil {
- return err
-
- }
- }
-
- result, err := modules.ContactGroups.List(s, params)
- if err != nil {
- return err
- }
-
- printList(result, modules.ContactGroups.GetColumns(s))
- return nil
- })
-
-}
diff --git a/cmd/climc/shell/notify/contacts.go b/cmd/climc/shell/notify/contacts.go
deleted file mode 100644
index d4d79a1045..0000000000
--- a/cmd/climc/shell/notify/contacts.go
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import (
- "yunion.io/x/jsonutils"
-
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/modulebase"
- "yunion.io/x/onecloud/pkg/mcclient/modules"
- "yunion.io/x/onecloud/pkg/mcclient/options"
-)
-
-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"`
- 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()
- tmpObj := jsonutils.NewDict()
- tmpObj.Add(jsonutils.NewString(args.CONTACTTYPE), "contact_type")
- tmpObj.Add(jsonutils.NewString(args.CONTACT), "contact")
- if len(args.Status) > 0 {
- if args.Status == "disable" {
- tmpObj.Add(jsonutils.NewInt(0), "enabled")
- } else {
- tmpObj.Add(jsonutils.NewInt(1), "enabled")
- }
- }
-
- arr.Add(tmpObj)
-
- params := jsonutils.NewDict()
- params.Add(arr, "contacts")
-
- pulls := jsonutils.NewArray()
- for _, pull := range args.Pull {
- pulls.Add(jsonutils.NewString(pull))
- }
- params.Add(pulls, "pull")
-
- contact, err := modules.Contacts.CustomizedPerformAction(s, args.UID, "update-contact", params)
- if err != nil {
- return err
- }
-
- printObject(contact)
- 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())
- params.Set("pull", jsonutils.NewArray(jsonutils.NewString(args.CONTACTTYPE)))
- contact, err := modules.Contacts.CustomizedPerformAction(s, args.UID, "update-contact", 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"`
- }
- R(&ContactsDeleteOptions{}, "contact-delete", "Delete contact for user", func(s *mcclient.ClientSession, args *ContactsDeleteOptions) error {
- arr := jsonutils.NewArray()
- tmpObj := jsonutils.NewDict()
- tmpObj.Add(jsonutils.NewString(args.CONTACTTYPE), "contact_type")
- tmpObj.Add(jsonutils.NewString(""), "contact")
- arr.Add(tmpObj)
- params := jsonutils.NewDict()
- params.Add(arr, "contacts")
- contact, err := modules.Contacts.CustomizedPerformAction(s, args.UID, "update-contact", params)
- if err != nil {
- return err
- }
- printObject(contact)
- return nil
- })
-
- /**
- * 获得所有用户的所有通信地址列表
- */
- type ContactsListOptions struct {
- options.BaseListOptions
- }
- R(&ContactsListOptions{}, "contact-list", "List all contacts for all users", func(s *mcclient.ClientSession, args *ContactsListOptions) error {
- var params *jsonutils.JSONDict
- {
- var err error
- params, err = args.BaseListOptions.Params()
- if err != nil {
- return err
-
- }
- }
- params.Add(jsonutils.JSONTrue, "details")
-
- result, err := modules.Contacts.List(s, params)
- if err != nil {
- return err
- }
-
- printList(result, modules.Contacts.GetColumns(s))
- return nil
- })
-
- /**
- * 获得一个用户全部通信地址
- */
- type ContactsListForUserOptions struct {
- options.BaseListOptions
- UID string `help:"The user you wanna find contact from (Keystone User ID)"`
- }
- R(&ContactsListForUserOptions{}, "contact-show", "List all contacts for the users", func(s *mcclient.ClientSession, args *ContactsListForUserOptions) error {
- var params *jsonutils.JSONDict
- {
- var err error
- params, err = args.BaseListOptions.Params()
- if err != nil {
- return err
-
- }
- }
-
- params.Add(jsonutils.JSONTrue, "details")
-
- result, err := modules.Contacts.CustomizedGet(s, args.UID, params)
- if err != nil {
- return err
- }
-
- contactsStr, err := result.GetString("details")
- if err != nil {
- return nil
- }
-
- contactsJson, err := jsonutils.ParseString(contactsStr)
- if err != nil {
- return err
- }
-
- contacts, err := contactsJson.GetArray()
- if err != nil {
- return err
- }
-
- printList(&modulebase.ListResult{Data: contacts}, nil)
- return nil
- })
-
- /**
- * 触发验证通信地址操作
- */
- type ContactsVerifyOptions struct {
- UID string `help:"The user you wanna verify contact for (Keystone User ID)"`
- CONTACT_TYPE string `help:"The contact type email|mobile"`
- CONTACT string `help:"The contacts details mobile number or email address"`
- }
- R(&ContactsVerifyOptions{}, "contact-verify-trigger", "Trigger contact verify", func(s *mcclient.ClientSession, args *ContactsVerifyOptions) error {
- 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)
- if err != nil {
- return err
- }
- return nil
- })
-
- type ContactsBatchDeleteOptions struct {
- UIDS []string `help:"All user'id you wanna to delete contacts (Keystone User ID)"`
- }
- R(&ContactsBatchDeleteOptions{}, "contact-delete", "Delete all contacts for the user", func(s *mcclient.ClientSession, args *ContactsBatchDeleteOptions) error {
- arr := jsonutils.NewArray()
- for _, f := range args.UIDS {
- arr.Add(jsonutils.NewString(f))
- }
-
- params := jsonutils.NewDict()
- params.Add(arr, "contacts")
-
- contact, err := modules.Contacts.DoBatchDeleteContacts(s, params)
-
- if err != nil {
- return err
- }
-
- printObject(contact)
- return nil
- })
-}
diff --git a/cmd/climc/shell/notify/email_config.go b/cmd/climc/shell/notify/email_config.go
deleted file mode 100644
index 5464b88bdc..0000000000
--- a/cmd/climc/shell/notify/email_config.go
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import (
- "yunion.io/x/jsonutils"
-
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/modules"
-)
-
-func init() {
-
- /**
- * 查询邮件配置信息
- */
- type EmailConfigShowOptions struct {
- TYPE string `help:"type "`
- }
- R(&EmailConfigShowOptions{}, "email-config-show", "Show email-config details",
- func(s *mcclient.ClientSession, args *EmailConfigShowOptions) error {
- result, err := modules.EmailConfigs.Get(s, args.TYPE, nil)
- if err != nil {
- return err
- }
- printObject(result)
- return nil
- })
-
- /**
- * 增加邮件配置信息
- */
- type EmailConfigCreateOptions struct {
- USERNAME string `help:"Username for email sender"`
- PASSWORD string `help:"Password for email sender"`
- HOSTNAME string `help:"Email server name"`
- SSLGLOBAL string `help:"use ssl_global"`
- HOSTPORT int64 `help:"Email server port"`
- }
-
- R(&EmailConfigCreateOptions{}, "email-config-create", "Create a Email Config",
- func(s *mcclient.ClientSession, args *EmailConfigCreateOptions) error {
- params := jsonutils.NewDict()
- params.Add(jsonutils.NewString(args.USERNAME), "username")
- params.Add(jsonutils.NewString(args.PASSWORD), "password")
- params.Add(jsonutils.NewString(args.HOSTNAME), "hostname")
- params.Add(jsonutils.NewString(args.SSLGLOBAL), "ssl_global")
- params.Add(jsonutils.NewInt(args.HOSTPORT), "hostport")
-
- result, err := modules.EmailConfigs.Create(s, params)
- if err != nil {
- return err
- }
- printObject(result)
- return nil
- })
-
- /**
- * 修改
- */
- type EmailConfigUpdateOptions struct {
- TYPE string `help:"type of email "`
- USERNAME string `help:"Username for email sender"`
- PASSWORD string `help:"Password for email sender"`
- HOSTNAME string `help:"Email server name"`
- SSLGLOBAL string `help:"use ssl_global"`
- HOSTPORT int64 `help:"Email server port"`
- }
- R(&EmailConfigUpdateOptions{}, "email-config-update", "Update a email-config", func(s *mcclient.ClientSession, args *EmailConfigUpdateOptions) error {
- params := jsonutils.NewDict()
- params.Add(jsonutils.NewString(args.USERNAME), "username")
- params.Add(jsonutils.NewString(args.PASSWORD), "password")
- params.Add(jsonutils.NewString(args.HOSTNAME), "hostname")
- params.Add(jsonutils.NewString(args.SSLGLOBAL), "ssl_global")
- params.Add(jsonutils.NewInt(args.HOSTPORT), "hostport")
-
- result, err := modules.EmailConfigs.Put(s, args.TYPE, params)
- if err != nil {
- return err
- }
- printObject(result)
- return nil
- })
-
- /**
- * 删除
- */
- type EmailConfigDeleteOptions struct {
- TYPE string `help:"type of email "`
- }
- R(&EmailConfigDeleteOptions{}, "email-config-delete", "Delete a email config", func(s *mcclient.ClientSession, args *EmailConfigDeleteOptions) error {
- result, e := modules.EmailConfigs.Delete(s, args.TYPE, nil)
- if e != nil {
- return e
- }
- printObject(result)
- return nil
- })
-
-}
diff --git a/cmd/climc/shell/notify/notification.go b/cmd/climc/shell/notify/notification.go
deleted file mode 100644
index 7491ba0e32..0000000000
--- a/cmd/climc/shell/notify/notification.go
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import (
- "yunion.io/x/jsonutils"
-
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/modules/notify"
- "yunion.io/x/onecloud/pkg/mcclient/options"
-)
-
-func init() {
-
- /**
- * 新建一个通知发送任务
- */
-
- type NotificationCreateOptions struct {
- 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"`
- Remark string `help:"Remark or description of the notification"`
- Group bool `help:"Send to group"`
- }
- type NotificationCreateSingleOptions struct {
- UID string `help:"The user you wanna sent to (Keystone User ID)"`
- NotificationCreateOptions
- }
-
- R(&NotificationCreateSingleOptions{}, "notify", "Send a notification to someone", func(s *mcclient.ClientSession,
- args *NotificationCreateSingleOptions) error {
-
- msg := notify.SNotifyMessage{}
- if args.Group {
- msg.Gid = []string{args.UID}
- } else {
- msg.Uid = []string{args.UID}
- }
-
- msg.ContactType = notify.TNotifyChannel(args.CONTACTTYPE)
- msg.Topic = args.TOPIC
- msg.Priority = notify.TNotifyPriority(args.PRIORITY)
- msg.Msg = args.MSG
- msg.Remark = args.Remark
-
- err := notify.Notifications.Send(s, msg)
- if err != nil {
- return err
- }
- return nil
- })
-
- type NotificationCreateMultiOptions struct {
- Uid []string `help:"The user you wanna sent to (Keystone User ID)"`
- NotificationCreateOptions
- }
-
- R(&NotificationCreateMultiOptions{}, "notify-batch", "Send a notification to someones",
- func(s *mcclient.ClientSession, args *NotificationCreateMultiOptions) error {
-
- msg := notify.SNotifyMessage{}
- if args.Group {
- msg.Gid = args.Uid
- } else {
- msg.Uid = args.Uid
- }
-
- msg.ContactType = notify.TNotifyChannel(args.CONTACTTYPE)
- msg.Topic = args.TOPIC
- msg.Priority = notify.TNotifyPriority(args.PRIORITY)
- msg.Msg = args.MSG
- msg.Remark = args.Remark
-
- err := notify.Notifications.Send(s, msg)
- if err != nil {
- return err
- }
- return nil
- })
-
- /**
- * 发送全局通知
- */
- type NotificationBroadcastOptions struct {
- // CONTACTTYPE string `help:"User's contacts type, cloud be email|mobile|dingtalk|/webconsole" choices:"email|mobile|dingtalk|webconsole"`
- Topic string `required:"true" help:"Title or topic of the notification"`
- Priority string `help:"Priority of the notification" choices:"normal|important|fatal" default:"normal"`
- Msg string `help:"The content of the notification"`
- Remark string `help:"Remark or description of the notification"`
- // Group bool `help:"Send to group"`
- }
-
- R(&NotificationBroadcastOptions{}, "notify-broadcast", "Send a notification to all online users", func(s *mcclient.ClientSession, args *NotificationBroadcastOptions) error {
- msg := notify.SNotifyMessage{}
- msg.Broadcast = true
- msg.ContactType = notify.NotifyByWebConsole
- msg.Topic = args.Topic
- msg.Priority = notify.TNotifyPriority(args.Priority)
- msg.Msg = args.Msg
- msg.Remark = args.Remark
-
- err := notify.Notifications.Send(s, msg)
- if err != nil {
- return err
- }
- return nil
- })
- /**
- * 修改通知发送任务的状态
- */
- type NotificationUpdateCallbackOptions struct {
- ID string `help:"ID of the notification send task"`
- STATUS string `help:"Notification send status" choices:"sent_ok|send_fail"`
- Remark string `help:"Remark or description of the operation or fail reason"`
- }
- R(&NotificationUpdateCallbackOptions{}, "notification-update-callback", "UpdateItem send status of the notification task", func(s *mcclient.ClientSession, args *NotificationUpdateCallbackOptions) error {
- params := jsonutils.NewDict()
- params.Add(jsonutils.NewString(args.STATUS), "status")
- if len(args.Remark) > 0 {
- params.Add(jsonutils.NewString(args.Remark), "remark")
- }
-
- notification, err := notify.Notifications.Put(s, args.ID, params)
- if err != nil {
- return err
- }
- printObject(notification)
- return nil
- })
-
- /**
- * 查询已发送的通知任务
- */
- type NotificationListOptions struct {
- options.BaseListOptions
- }
- R(&NotificationListOptions{}, "notify-list", "List notification history", func(s *mcclient.ClientSession, args *NotificationListOptions) error {
- result, err := notify.Notifications.List(s, nil)
- if err != nil {
- return err
- }
-
- printList(result, notify.Notifications.GetColumns(s))
- return nil
- })
-
-}
diff --git a/cmd/climc/shell/notify/notify.go b/cmd/climc/shell/notify/notify.go
deleted file mode 100644
index 21b59dddc9..0000000000
--- a/cmd/climc/shell/notify/notify.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import (
- "yunion.io/x/jsonutils"
-
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/modules"
-)
-
-func init() {
-
- type ConfigCreate2Options struct {
- CONTACTTYPE string `help:"contact type (email|sms_aliyun or others)"`
- CONFIGS []string `help:"config (k, v)"`
- }
- R(&ConfigCreate2Options{}, "notify-config-update", "config update, example: notify_config-update email mail.smtp.hostname hostname mail.smtp.hostport 123.", func(s *mcclient.ClientSession, args *ConfigCreate2Options) error {
- tmp := jsonutils.NewDict()
- for i := 0; i+1 < len(args.CONFIGS); i += 2 {
- tmp.Add(jsonutils.NewString(args.CONFIGS[i+1]), args.CONFIGS[i])
- }
- body := jsonutils.NewDict()
- body.Add(tmp, args.CONTACTTYPE)
- ret, err := modules.Configs.Create(s, body)
- if err != nil {
- return err
- }
- printObject(ret)
- return nil
- })
- R(&ConfigCreate2Options{}, "notify-config-validate", "config validate", func(s *mcclient.ClientSession,
- args *ConfigCreate2Options) error {
- tmp := jsonutils.NewDict()
- for i := 0; i+1 < len(args.CONFIGS); i += 2 {
- tmp.Add(jsonutils.NewString(args.CONFIGS[i+1]), args.CONFIGS[i])
- }
- ret, err := modules.Configs.PerformAction(s, args.CONTACTTYPE, "validate", tmp)
- if err != nil {
- return err
- }
- printObject(ret)
- return nil
- })
-
- type ConfigGet2Options struct {
- TYPE string `help:"contact type (email|sms_aliyun or others)"`
- }
- R(&ConfigGet2Options{}, "notify-config-show", "config show", func(s *mcclient.ClientSession, args *ConfigGet2Options) error {
- result, err := modules.Configs.Get(s, args.TYPE, jsonutils.JSONNull)
- if err != nil {
- return err
- }
- printObject(result)
- return nil
- })
- R(&ConfigGet2Options{}, "notify-config-delete", "config delete", func(s *mcclient.ClientSession, args *ConfigGet2Options) error {
- result, err := modules.Configs.Delete(s, args.TYPE, jsonutils.JSONNull)
- if err != nil {
- return err
- }
- printObject(result)
- return nil
- })
-}
diff --git a/cmd/climc/shell/notify/notify_template.go b/cmd/climc/shell/notify/notify_template.go
deleted file mode 100644
index 2a0581e761..0000000000
--- a/cmd/climc/shell/notify/notify_template.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import (
- "yunion.io/x/jsonutils"
-
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/modules"
- "yunion.io/x/onecloud/pkg/mcclient/options"
-)
-
-func init() {
- type NotifyTemplateUpdateOptions struct {
- CONTACTTYPE string `help:"the contanct type, such as 'email', 'mobile'"`
- TOPIC string `help:"the topic of temlate, such as 'VERIFY', 'ALARM'"`
- TEMPLATETYPE string `help:"the type of template" choices:"content|title|remote"`
- CONTENT string `help:"the content of template"`
- }
- R(&NotifyTemplateUpdateOptions{}, "notify-template-update", "Create, update contact for user", func(s *mcclient.ClientSession,
- args *NotifyTemplateUpdateOptions) error {
- arr := jsonutils.NewArray()
- tmpObj := jsonutils.NewDict()
- tmpObj.Add(jsonutils.NewString(args.TOPIC), "topic")
- tmpObj.Add(jsonutils.NewString(args.TEMPLATETYPE), "template_type")
- tmpObj.Add(jsonutils.NewString(args.CONTENT), "content")
- arr.Add(tmpObj)
-
- params := jsonutils.NewDict()
- params.Add(arr, "notifytemplates")
-
- contact, err := modules.NotifyTemplates.PerformAction(s, args.CONTACTTYPE, "update-template", params)
-
- if err != nil {
- return err
- }
-
- printObject(contact)
- return nil
- })
-
- type NotifyTemplateDeleteOptions struct {
- CONTACTTYPE string `help:"the contanct type, such as 'email', 'mobile'"`
- Topic string `help:"the topic of temlate, such as 'VERIFY', 'ALARM'"`
- }
-
- R(&NotifyTemplateDeleteOptions{}, "notify-template-delete", "delete notify template",
- func(s *mcclient.ClientSession, args *NotifyTemplateDeleteOptions) error {
-
- tmpObj := jsonutils.NewDict()
- tmpObj.Add(jsonutils.NewString(args.CONTACTTYPE), "contact_type")
- tmpObj.Add(jsonutils.NewString(args.Topic), "topic")
- _, err := modules.NotifyTemplates.DeleteContents(s, tmpObj)
- if err != nil {
- return err
- }
- return nil
- })
-
- type NotifyTemplateListOptions struct {
- options.BaseListOptions
- }
-
- R(&NotifyTemplateListOptions{}, "notify-template-list", "List all notify template",
- func(s *mcclient.ClientSession, args *NotifyTemplateListOptions) error {
-
- params, err := args.BaseListOptions.Params()
- if err != nil {
- return err
- }
- result, err := modules.NotifyTemplates.List(s, params)
- if err != nil {
- return err
- }
- printList(result, modules.NotifyTemplates.GetColumns(s))
- return nil
- })
-}
diff --git a/cmd/climc/shell/notify/sms_configs.go b/cmd/climc/shell/notify/sms_configs.go
deleted file mode 100644
index c6eb8d3f88..0000000000
--- a/cmd/climc/shell/notify/sms_configs.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import (
- "yunion.io/x/jsonutils"
-
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/modules"
-)
-
-func init() {
-
- /**
- * 查询短信配置信息
- */
- type SmsConfigShowOptions struct {
- TYPE string `help:"type "`
- }
- R(&SmsConfigShowOptions{}, "sms-config-show", "Show sms config details",
- func(s *mcclient.ClientSession, args *SmsConfigShowOptions) error {
- result, err := modules.SmsConfigs.Get(s, args.TYPE, nil)
- if err != nil {
- return err
- }
- printObject(result)
- return nil
- })
-
- /**
- * 增加短信配置信息
- */
- type SmsConfigCreateOptions struct {
- TYPE string `help:"sms vendor"`
- ACCESSKEYID string `help:"ACCESSKEYID for sms vendor"`
- ACCESSKEYSECRET string `help:"ACCESSKEYSECRET for sms vendor"`
- SIGNATURE string `help:"SIGNATURE for sms vendor"`
- SmsTemplateOne string `help:"Sms TemplateOne"`
- SmsTemplateTwo string `help:"Sms TemplateTwo"`
- SmsTemplateThree string `help:"Sms TemplateThree"`
- SmsCheckCode string `help:"Sms Check Code "`
- }
-
- R(&SmsConfigCreateOptions{}, "sms-config-create", "Create a sms Config",
- func(s *mcclient.ClientSession, args *SmsConfigCreateOptions) error {
- params := jsonutils.NewDict()
- params.Add(jsonutils.NewString(args.TYPE), "type")
- params.Add(jsonutils.NewString(args.ACCESSKEYID), "access_key_id")
- params.Add(jsonutils.NewString(args.ACCESSKEYSECRET), "access_key_secret")
- params.Add(jsonutils.NewString(args.SIGNATURE), "signature")
- params.Add(jsonutils.NewString(args.SmsTemplateOne), "sms_template_one")
- params.Add(jsonutils.NewString(args.SmsTemplateTwo), "sms_template_two")
- params.Add(jsonutils.NewString(args.SmsTemplateThree), "sms_template_three")
- params.Add(jsonutils.NewString(args.SmsCheckCode), "sms_check_code")
-
- result, err := modules.SmsConfigs.Create(s, params)
- if err != nil {
- return err
- }
- printObject(result)
- return nil
- })
-
- /**
- * 修改
- */
- type SmsConfigUpdateOptions struct {
- TYPE string `help:"sms vendor"`
- ACCESSKEYID string `help:"ACCESSKEYID for sms vendor"`
- ACCESSKEYSECRET string `help:"ACCESSKEYSECRET for sms vendor"`
- SIGNATURE string `help:"SIGNATURE for sms vendor"`
- SmsTemplateOne string `help:"Sms TemplateOne"`
- SmsTemplateTwo string `help:"Sms TemplateTwo"`
- SmsTemplateThree string `help:"Sms TemplateThree"`
- SmsCheckCode string `help:"Sms Check Code "`
- }
- R(&SmsConfigUpdateOptions{}, "sms-config-update", "Update a sms-config", func(s *mcclient.ClientSession, args *SmsConfigUpdateOptions) error {
- params := jsonutils.NewDict()
-
- params.Add(jsonutils.NewString(args.TYPE), "type")
- params.Add(jsonutils.NewString(args.ACCESSKEYID), "access_key_id")
- params.Add(jsonutils.NewString(args.ACCESSKEYSECRET), "access_key_secret")
- params.Add(jsonutils.NewString(args.SIGNATURE), "signature")
- params.Add(jsonutils.NewString(args.SmsTemplateOne), "sms_template_one")
- params.Add(jsonutils.NewString(args.SmsTemplateTwo), "sms_template_two")
- params.Add(jsonutils.NewString(args.SmsTemplateThree), "sms_template_three")
- params.Add(jsonutils.NewString(args.SmsCheckCode), "sms_check_code")
-
- result, err := modules.SmsConfigs.Put(s, args.TYPE, params)
- if err != nil {
- return err
- }
- printObject(result)
- return nil
- })
-
- /**
- * 删除
- */
- type SmsConfigDeleteOptions struct {
- TYPE string `help:"sms vendor"`
- }
- R(&SmsConfigDeleteOptions{}, "sms-config-delete", "Delete a sms config", func(s *mcclient.ClientSession, args *SmsConfigDeleteOptions) error {
- result, e := modules.SmsConfigs.Delete(s, args.TYPE, nil)
- if e != nil {
- return e
- }
- printObject(result)
- return nil
- })
-
-}
diff --git a/cmd/climc/shell/notify/verification.go b/cmd/climc/shell/notify/verification.go
deleted file mode 100644
index f65fcf44a8..0000000000
--- a/cmd/climc/shell/notify/verification.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import (
- "yunion.io/x/jsonutils"
-
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/modules"
-)
-
-func init() {
-
- /**
- * 通信地址验证
- */
- type ContactsVerifyOptions struct {
- ID string `help:"Verification process ID"`
- TOKEN string `help:"Temporary token issued to the user when the validation is triggered"`
- }
- R(&ContactsVerifyOptions{}, "contact-verify", "Trigger contact verify", func(s *mcclient.ClientSession, args *ContactsVerifyOptions) error {
- params := jsonutils.NewDict()
- params.Add(jsonutils.NewString(args.TOKEN), "token")
-
- result, err := modules.Verifications.Get(s, args.ID, params)
-
- if err != nil {
- return err
- }
-
- printObject(result)
- return nil
- })
-
-}
diff --git a/cmd/climc/shell/notify/common.go b/cmd/climc/shell/notifyv2/common.go
similarity index 98%
rename from cmd/climc/shell/notify/common.go
rename to cmd/climc/shell/notifyv2/common.go
index 175530d019..235bc49899 100644
--- a/cmd/climc/shell/notify/common.go
+++ b/cmd/climc/shell/notifyv2/common.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package notify
+package notifyv2
import (
"yunion.io/x/onecloud/cmd/climc/shell"
diff --git a/cmd/climc/shell/notifyv2/config.go b/cmd/climc/shell/notifyv2/config.go
new file mode 100644
index 0000000000..9a2e494a62
--- /dev/null
+++ b/cmd/climc/shell/notifyv2/config.go
@@ -0,0 +1,123 @@
+// Copyright 2019 Yunion
+//
+// 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 notifyv2
+
+import (
+ "strings"
+
+ "yunion.io/x/jsonutils"
+
+ "yunion.io/x/onecloud/pkg/mcclient"
+ "yunion.io/x/onecloud/pkg/mcclient/modules"
+ "yunion.io/x/onecloud/pkg/mcclient/options"
+)
+
+func init() {
+ type ConfigListOptions struct {
+ options.BaseListOptions
+ }
+ R(&ConfigListOptions{}, "notify-config-list", "List notify config", func(s *mcclient.ClientSession, args *ConfigListOptions) error {
+ params, err := options.ListStructToParams(args)
+ if err != nil {
+ return err
+ }
+ result, err := modules.NotifyConfig.List(s, params)
+ if err != nil {
+ return err
+ }
+ printList(result, modules.NotifyConfig.GetColumns(s))
+ return nil
+ })
+ type ConfigCreateOptions struct {
+ TYPE string `help:"Type contact config"`
+ Configs []string `help:"Config content, format: 'key:value'"`
+ }
+ R(&ConfigCreateOptions{}, "notify-config-create", "Create notify config", func(s *mcclient.ClientSession, args *ConfigCreateOptions) error {
+ configs := jsonutils.NewDict()
+ for _, kv := range args.Configs {
+ index := strings.IndexByte(kv, ':')
+ configs.Set(kv[:index], jsonutils.NewString(kv[index+1:]))
+ }
+ params := jsonutils.NewDict()
+ params.Set("type", jsonutils.NewString(args.TYPE))
+ params.Set("content", configs)
+ ret, err := modules.NotifyConfig.Create(s, params)
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ R(&ConfigCreateOptions{}, "notify-config-update", "Update notify config", func(s *mcclient.ClientSession, args *ConfigCreateOptions) error {
+ configs := jsonutils.NewDict()
+ for _, kv := range args.Configs {
+ index := strings.IndexByte(kv, ':')
+ configs.Set(kv[:index], jsonutils.NewString(kv[index+1:]))
+ }
+ params := jsonutils.NewDict()
+ params.Set("content", configs)
+
+ id, err := configIdFromType(s, args.TYPE)
+ if err != nil {
+ return err
+ }
+ ret, err := modules.NotifyConfig.Update(s, id, params)
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ type ConfigOptions struct {
+ TYPE string `help:"Type contact config"`
+ }
+ R(&ConfigOptions{}, "notify-config-delete", "Delete notify config", func(s *mcclient.ClientSession, args *ConfigOptions) error {
+ id, err := configIdFromType(s, args.TYPE)
+ if err != nil {
+ return err
+ }
+ ret, err := modules.NotifyConfig.Delete(s, id, nil)
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ R(&ConfigOptions{}, "notify-config-show", "Show notify config", func(s *mcclient.ClientSession, args *ConfigOptions) error {
+ listParams := jsonutils.NewDict()
+ listParams.Set("type", jsonutils.NewString(args.TYPE))
+ list, err := modules.NotifyConfig.List(s, listParams)
+ if err != nil {
+ return err
+ }
+ data := list.Data[0]
+ printObject(data)
+ return nil
+ })
+}
+
+func configIdFromType(s *mcclient.ClientSession, t string) (string, error) {
+ listParams := jsonutils.NewDict()
+ listParams.Set("type", jsonutils.NewString(t))
+ list, err := modules.NotifyConfig.List(s, listParams)
+ if err != nil {
+ return "", err
+ }
+ id, err := list.Data[0].GetString("id")
+ if err != nil {
+ return "", err
+ }
+ return id, nil
+}
diff --git a/cmd/climc/shell/notifyv2/notification.go b/cmd/climc/shell/notifyv2/notification.go
new file mode 100644
index 0000000000..e9d0940a37
--- /dev/null
+++ b/cmd/climc/shell/notifyv2/notification.go
@@ -0,0 +1,78 @@
+// Copyright 2019 Yunion
+//
+// 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 notifyv2
+
+import (
+ "yunion.io/x/jsonutils"
+
+ api "yunion.io/x/onecloud/pkg/apis/notify"
+ "yunion.io/x/onecloud/pkg/mcclient"
+ "yunion.io/x/onecloud/pkg/mcclient/modules"
+ "yunion.io/x/onecloud/pkg/mcclient/options"
+)
+
+func init() {
+ type NotificationCreateInput struct {
+ Receivers []string `help:"ID or Name of Receiver"`
+ ContactType string `help:"Contact type of receiver"`
+ TOPIC string `help:"Topic"`
+ Priority string `help:"Priority"`
+ MESSAGE string `help:"Message"`
+ }
+ R(&NotificationCreateInput{}, "notify-send", "Send a notify message", func(s *mcclient.ClientSession, args *NotificationCreateInput) error {
+ input := api.NotificationCreateInput{
+ Receivers: args.Receivers,
+ ContactType: args.ContactType,
+ Topic: args.TOPIC,
+ Priority: args.Priority,
+ Message: args.MESSAGE,
+ }
+ ret, err := modules.Notification.Create(s, jsonutils.Marshal(input))
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ type NotificationInput struct {
+ ID string `help:"Id of notification"`
+ }
+ R(&NotificationInput{}, "notify-show", "Show a notify message", func(s *mcclient.ClientSession, args *NotificationInput) error {
+ ret, err := modules.Notification.Get(s, args.ID, nil)
+ if err != nil {
+ return nil
+ }
+ printObject(ret)
+ return nil
+ })
+ type NotificationListInput struct {
+ options.BaseListOptions
+
+ ContactType string `help:"contact_type"`
+ ReceiverId string `help:"receiver_id"`
+ }
+ R(&NotificationListInput{}, "notify-list", "List notify message", func(s *mcclient.ClientSession, args *NotificationListInput) error {
+ params, err := options.ListStructToParams(args)
+ if err != nil {
+ return err
+ }
+ ret, err := modules.Notification.List(s, params)
+ if err != nil {
+ return err
+ }
+ printList(ret, modules.Notification.GetColumns(s))
+ return nil
+ })
+}
diff --git a/cmd/climc/shell/notifyv2/receiver.go b/cmd/climc/shell/notifyv2/receiver.go
new file mode 100644
index 0000000000..c78132d4bd
--- /dev/null
+++ b/cmd/climc/shell/notifyv2/receiver.go
@@ -0,0 +1,165 @@
+// Copyright 2019 Yunion
+//
+// 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 notifyv2
+
+import (
+ "yunion.io/x/jsonutils"
+
+ "yunion.io/x/onecloud/pkg/mcclient"
+ "yunion.io/x/onecloud/pkg/mcclient/modules"
+ "yunion.io/x/onecloud/pkg/mcclient/options"
+)
+
+func init() {
+ type ReceiverListOptions struct {
+ options.BaseListOptions
+ UId string `help:"user id in keystone"`
+ UName string `help:"user name in keystone"`
+ EnabledContactType string `help:"enabled contact type"`
+ VerifiedContactType string `help:"verified contact type"`
+ }
+ R(&ReceiverListOptions{}, "notify-receiver-list", "List notify receiver", func(s *mcclient.ClientSession, args *ReceiverListOptions) error {
+ params, err := options.ListStructToParams(args)
+ if err != nil {
+ return err
+ }
+ result, err := modules.NotifyReceiver.List(s, params)
+ if err != nil {
+ return err
+ }
+ printList(result, modules.NotifyReceiver.GetColumns(s))
+ return nil
+ })
+ type ReceiverCreateOptions struct {
+ UID string `help:"user id in keystone"`
+ Email string `help:"email of receiver"`
+ Mobile string `help:"mobile of receiver"`
+ EnabledContactTypes []string `help:"enabled contact type"`
+ }
+ R(&ReceiverCreateOptions{}, "notify-receiver-create", "Create notify receiver", func(s *mcclient.ClientSession, args *ReceiverCreateOptions) error {
+ params := jsonutils.Marshal(args).(*jsonutils.JSONDict)
+ receiver, err := modules.NotifyReceiver.Create(s, params)
+ if err != nil {
+ return err
+ }
+ printObject(receiver)
+ return nil
+ })
+ type ReceiverUpdateInput struct {
+ ID string `help:"Id or Name of receiver"`
+ Email string `help:"email of receiver"`
+ Mobile string `help:"mobile of receiver"`
+ EnabledContactType []string `help:"enabled contact type"`
+ }
+ R(&ReceiverUpdateInput{}, "notify-receiver-update", "Update notify receiver", func(s *mcclient.ClientSession, args *ReceiverUpdateInput) error {
+ params := jsonutils.NewDict()
+ if len(args.Email) > 0 {
+ params.Set("email", jsonutils.NewString(args.Email))
+ }
+ if len(args.Mobile) > 0 {
+ params.Set("mobile", jsonutils.NewString(args.Mobile))
+ }
+ if len(args.EnabledContactType) > 0 {
+ params.Set("enabled_contact_types", jsonutils.NewStringArray(args.EnabledContactType))
+ }
+ ret, err := modules.NotifyReceiver.Update(s, args.ID, params)
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ type ReceiverOptions struct {
+ ID string `help:"Id or Name of receiver"`
+ }
+ R(&ReceiverOptions{}, "notify-receiver-show", "Show notify receiver", func(s *mcclient.ClientSession, args *ReceiverOptions) error {
+ ret, err := modules.NotifyReceiver.Get(s, args.ID, nil)
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ R(&ReceiverOptions{}, "notify-receiver-delete", "Delete notify receiver", func(s *mcclient.ClientSession, args *ReceiverOptions) error {
+ receiver, err := modules.NotifyReceiver.Delete(s, args.ID, jsonutils.NewDict())
+ if err != nil {
+ return err
+ }
+ printObject(receiver)
+ return nil
+ })
+ R(&ReceiverOptions{}, "notify-receiver-enable", "Enable notify receiver", func(s *mcclient.ClientSession, args *ReceiverOptions) error {
+ ret, err := modules.NotifyReceiver.PerformAction(s, args.ID, "enable", jsonutils.NewDict())
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ R(&ReceiverOptions{}, "notify-receiver-disable", "Disable notify receiver", func(s *mcclient.ClientSession, args *ReceiverOptions) error {
+ ret, err := modules.NotifyReceiver.PerformAction(s, args.ID, "disable", jsonutils.NewDict())
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ type ReceiverTriggerVerifyInput struct {
+ ID string `help:"Id or Name of receiver"`
+ ContactType string `help:"Contact type to trigger verify" choices:"email|mobile"`
+ }
+ R(&ReceiverTriggerVerifyInput{}, "notify-receiver-trigger-verify", "Trigger verification for receiver about some contact", func(s *mcclient.ClientSession, args *ReceiverTriggerVerifyInput) error {
+ params := jsonutils.NewDict()
+ params.Set("contact_type", jsonutils.NewString(args.ContactType))
+ ret, err := modules.NotifyReceiver.PerformAction(s, args.ID, "trigger-verify", params)
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ type ReceiverVerifyInput struct {
+ ID string `help:"Id or Name of receiver"`
+ ContactType string `help:"Contact type to trigger verify" choices:"email|mobile"`
+ Token string `help:"Token from verify message sent to you"`
+ }
+ R(&ReceiverVerifyInput{}, "notify-receiver-verify", "Verify receiver about some contact type", func(s *mcclient.ClientSession, args *ReceiverVerifyInput) error {
+ params := jsonutils.NewDict()
+ params.Set("contact_type", jsonutils.NewString(args.ContactType))
+ params.Set("token", jsonutils.NewString(args.Token))
+ ret, err := modules.NotifyReceiver.PerformAction(s, args.ID, "verify", params)
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ R(&ReceiverOptions{}, "notify-receiver-enable", "Enable receiver", func(s *mcclient.ClientSession, args *ReceiverOptions) error {
+ ret, err := modules.NotifyReceiver.PerformAction(s, args.ID, "enable", nil)
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ R(&ReceiverOptions{}, "notify-receiver-disable", "Disable receiver", func(s *mcclient.ClientSession, args *ReceiverOptions) error {
+ ret, err := modules.NotifyReceiver.PerformAction(s, args.ID, "disable", nil)
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+}
diff --git a/cmd/climc/shell/notifyv2/template.go b/cmd/climc/shell/notifyv2/template.go
new file mode 100644
index 0000000000..a74d13d64e
--- /dev/null
+++ b/cmd/climc/shell/notifyv2/template.go
@@ -0,0 +1,106 @@
+// Copyright 2019 Yunion
+//
+// 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 notifyv2
+
+import (
+ "yunion.io/x/jsonutils"
+
+ api "yunion.io/x/onecloud/pkg/apis/notify"
+ "yunion.io/x/onecloud/pkg/mcclient"
+ "yunion.io/x/onecloud/pkg/mcclient/modules"
+ "yunion.io/x/onecloud/pkg/mcclient/options"
+)
+
+func init() {
+ type TemplateCreateInput struct {
+ NAME string `help:"Name"`
+ ContactType string `help:"Contact type, specifically, setting it to all means all contact type"`
+ TemplateType string `help:"Template type"`
+ Topic string `help:"Template topic"`
+ Content string `help:"Template content"`
+ Example string `help:"Example for using this template"`
+ }
+ R(&TemplateCreateInput{}, "notify-template-create", "Create notify template", func(s *mcclient.ClientSession, args *TemplateCreateInput) error {
+ input := api.TemplateCreateInput{
+ ContactType: args.ContactType,
+ TemplateType: args.TemplateType,
+ Topic: args.Topic,
+ Content: args.Content,
+ Example: args.Example,
+ }
+ input.Name = args.NAME
+ ret, err := modules.NotifyTemplate.Create(s, jsonutils.Marshal(input))
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ type TemplateListInput struct {
+ options.BaseListOptions
+
+ ContactType string `help:"Contact type"`
+ TemplateType string `help:"Template type"`
+ Topic string `help:"Topic"`
+ }
+ R(&TemplateListInput{}, "notify-template-list", "List notify template", func(s *mcclient.ClientSession, args *TemplateListInput) error {
+ params, err := options.ListStructToParams(args)
+ if err != nil {
+ return err
+ }
+ list, err := modules.NotifyTemplate.List(s, params)
+ if err != nil {
+ return err
+ }
+ printList(list, modules.NotifyTemplate.GetColumns(s))
+ return nil
+ })
+ type TemplateInput struct {
+ ID string `help:"id or name of template"`
+ }
+ R(&TemplateInput{}, "notify-template-get", "Get notify template", func(s *mcclient.ClientSession, args *TemplateInput) error {
+ ret, err := modules.NotifyTemplate.Get(s, args.ID, nil)
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ type TemplateUpdateInput struct {
+ ID string `help:"id or name of template"`
+ Content string `help:"Template content"`
+ Example string `help:"Example for using this template"`
+ }
+ R(&TemplateUpdateInput{}, "notify-template-update", "Update notify template", func(s *mcclient.ClientSession, args *TemplateUpdateInput) error {
+ input := api.TemplateUpdateInput{
+ Content: args.Content,
+ Example: args.Example,
+ }
+ ret, err := modules.NotifyTemplate.Update(s, args.ID, jsonutils.Marshal(input))
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+ R(&TemplateInput{}, "notify-template-delete", "Delete notify template", func(s *mcclient.ClientSession, args *TemplateInput) error {
+ ret, err := modules.NotifyTemplate.Delete(s, args.ID, nil)
+ if err != nil {
+ return err
+ }
+ printObject(ret)
+ return nil
+ })
+}
diff --git a/cmd/notify/main.go b/cmd/notify/main.go
index 0a733e2679..6a499c2283 100644
--- a/cmd/notify/main.go
+++ b/cmd/notify/main.go
@@ -15,9 +15,9 @@
package main
import (
- "yunion.io/x/onecloud/pkg/notify"
+ "yunion.io/x/onecloud/pkg/notify/service"
)
func main() {
- notify.StartService()
+ service.StartService()
}
diff --git a/pkg/apis/notify/config.go b/pkg/apis/notify/config.go
new file mode 100644
index 0000000000..84bd2b76b4
--- /dev/null
+++ b/pkg/apis/notify/config.go
@@ -0,0 +1,63 @@
+// Copyright 2019 Yunion
+//
+// 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 notify
+
+import (
+ "yunion.io/x/jsonutils"
+
+ "yunion.io/x/onecloud/pkg/apis"
+)
+
+type ConfigCreateInput struct {
+ apis.StandaloneResourceCreateInput
+
+ // description: config type
+ // required: true
+ // example: feishu
+ Type string `json:"type"`
+
+ // description: config content
+ // required: true
+ // example: {"app_id": "123456", "app_secret": "feishu_nihao"}
+ Content jsonutils.JSONObject `json:"content"`
+}
+
+type ConfigDetails struct {
+ apis.StandaloneResourceDetails
+
+ SConfig
+}
+
+type ConfigListInput struct {
+ apis.StandaloneResourceListInput
+ Type string `json:"type"`
+}
+
+type ConfigValidateInput struct {
+ // description: config type
+ // required: true
+ // example: feishu
+ Type string `json:"type"`
+
+ // description: config content
+ // required: true
+ // example: {"app_id": "123456", "app_secret": "feishu_nihao"}
+ Content jsonutils.JSONObject `json:"content"`
+}
+
+type ConfigValidateOutput struct {
+ IsValid bool `json:"is_valid"`
+ Message string `json:"message"`
+}
diff --git a/pkg/apis/notify/const.go b/pkg/apis/notify/const.go
new file mode 100644
index 0000000000..3a2d0ac6e0
--- /dev/null
+++ b/pkg/apis/notify/const.go
@@ -0,0 +1,63 @@
+// Copyright 2019 Yunion
+//
+// 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 notify
+
+import "yunion.io/x/onecloud/pkg/apis"
+
+const (
+ SERVICE_TYPE = apis.SERVICE_TYPE_NOTIFY
+ SERVICE_VERSION = ""
+
+ EMAIL = "email"
+ MOBILE = "mobile"
+ DINGTALK = "dingtalk"
+ FEISHU = "feishu"
+ WEBCONSOLE = "webconsole"
+ WORKWX = "workwx"
+ FEISHU_ROBOT = "feishu-robot"
+ DINGTALK_ROBOT = "dingtalk-robot"
+ WORKWX_ROBOT = "workwx-robot"
+
+ ROBOT = "robot"
+
+ RECEIVER_NOTIFICATION_RECEIVED = "received" // Received a task about sending a notification
+ RECEIVER_NOTIFICATION_SENT = "sending" // Nofity module has sent notification, but result unkown
+ RECEIVER_NOTIFICATION_OK = "sent_ok" // Notification was sent successfully
+ RECEIVER_NOTIFICATION_FAIL = "sent_fail" // That sent a notification is failed
+
+ VERIFICATION_SENT = "sent" // Verification was sent
+ VERIFICATION_SENT_FAIL = "sent_fail" // Verification was sent failed
+ VERIFICATION_VERIFIED = "verified" // Verification was verified
+ VERIFICATION_TOKEN_EXPIRED = "Verification code expired"
+ VERIFICATION_TOKEN_INVALID = "Incorrect verification code"
+
+ RECEIVER_STATUS_READY = "ready"
+ RECEIVER_STATUS_PULLING = "pulling"
+ RECEIVER_STATUS_PULL_FAILED = "pull_failed"
+
+ NOTIFICATION_PRIORITY_IMPORTANT = "important"
+ NOTIFICATION_PRIORITY_CRITICAL = "fatal"
+ NOTIFICATION_PRIORITY_NORMAL = "normal"
+
+ NOTIFICATION_STATUS_RECEIVED = "received"
+ NOTIFICATION_STATUS_SENDING = "sending"
+ NOTIFICATION_STATUS_FAILED = "failed"
+ NOTIFICATION_STATUS_OK = "ok"
+ NOTIFICATION_STATUS_PART_OK = "part_ok"
+
+ TEMPLATE_TYPE_TITLE = "title"
+ TEMPLATE_TYPE_CONTENT = "content"
+ TEMPLATE_TYPE_REMOTE = "remote"
+)
diff --git a/pkg/apis/notify/contact.go b/pkg/apis/notify/contact.go
deleted file mode 100644
index 05f618bc1a..0000000000
--- a/pkg/apis/notify/contact.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import "yunion.io/x/onecloud/pkg/apis"
-
-type ContactDetails struct {
- apis.ResourceBaseDetails
-
- UID string `json:"uid"`
- Name string `json:"name"`
- Details string `json:"details"`
-}
diff --git a/pkg/apis/notify/notification.go b/pkg/apis/notify/notification.go
index 96fc3ddad7..c3dead2c70 100644
--- a/pkg/apis/notify/notification.go
+++ b/pkg/apis/notify/notification.go
@@ -15,18 +15,62 @@
package notify
import (
- "yunion.io/x/jsonutils"
+ "time"
"yunion.io/x/onecloud/pkg/apis"
)
-type NotificationDetails struct {
- apis.ResourceBaseDetails
+type NotificationCreateInput struct {
+ apis.StatusStandaloneResourceCreateInput
- UserList jsonutils.JSONObject `json:"user_list"`
+ // description: ids or names of receiver
+ // required: false
+ // example: {"adfb720ccdd34c638346ea4fa7a713a8", "zhangsan"}
+ Receivers []string `json:"receivers"`
+ // description: direct contact, admin privileges required
+ // required: false
+ Contacts []string `json:"contacts"`
+ // description: contact type
+ // required: ture
+ // example: email
+ ContactType string `json:"contact_type"`
+ // description: notification topic
+ // required: true
+ // example: IMAGE_ACTIVE
+ Topic string `json:"topic"`
+ // description: notification priority
+ // required: false
+ // enum: fatal,important,nomal
+ // example: normal
+ Priority string `json:"priority"`
+ // description: message content or jsonobject
+ // required: ture
+ Message string `json:"message"`
+}
+
+type ReceiveDetail struct {
+ ReceiverId string `json:"receiver_id"`
+ ReceiverName string `json:"receiver_name"`
+ Contact string `json:"contact"`
+ SendAt time.Time `json:"sendAt"`
+ SendBy string `json:"send_by"`
+ Status string `json:"status"`
+ FailedReason string `json:"failed_reason"`
+}
+
+type NotificationDetails struct {
+ apis.StatusStandaloneResourceDetails
+
+ SNotification
+
+ Title string `json:"title"`
+ Content string `json:"content"`
+ ReceiveDetails []ReceiveDetail `json:"receive_details"`
}
type NotificationListInput struct {
- Scope string `json:"scope"`
- ContactType string `json:"contact_type"`
+ apis.StatusStandaloneResourceListInput
+
+ ContactType string
+ ReceiverId string
}
diff --git a/pkg/apis/notify/notify.go b/pkg/apis/notify/notify.go
deleted file mode 100644
index 58e3ef7838..0000000000
--- a/pkg/apis/notify/notify.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import "yunion.io/x/onecloud/pkg/apis"
-
-const (
- SERVICE_TYPE = apis.SERVICE_TYPE_NOTIFY
- SERVICE_VERSION = ""
-)
diff --git a/pkg/apis/notify/receiver.go b/pkg/apis/notify/receiver.go
new file mode 100644
index 0000000000..500af9f005
--- /dev/null
+++ b/pkg/apis/notify/receiver.go
@@ -0,0 +1,110 @@
+// Copyright 2019 Yunion
+//
+// 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 notify
+
+import (
+ "yunion.io/x/onecloud/pkg/apis"
+)
+
+type ReceiverCreateInput struct {
+ apis.StatusStandaloneResourceCreateInput
+ apis.DomainizedResourceCreateInput
+ apis.EnabledBaseResourceCreateInput
+
+ // description: user id in keystone
+ // example: adfb720ccdd34c638346ea4fa7a713a8
+ UID string `json:"uid"`
+
+ // description: user name in keystone
+ // example: hello
+ UName string `json:"uname"`
+
+ // description: user email
+ // example: example@gmail.com
+ Email string `json:"email"`
+
+ // description: user mobile
+ // example: 17812345678
+ Mobile string `json:"mobile"`
+
+ // description: enabled contact types for user
+ // example: {"email", "mobile", "feishu", "dingtalk", "workwx"}
+ EnabledContactTypes []string `json:"enabled_contact_types"`
+}
+
+type ReceiverDetails struct {
+ apis.StatusStandaloneResourceDetails
+ apis.DomainizedResourceInfo
+
+ SReceiver
+
+ // description: enabled contact types for user
+ // example: eamil, mobile, feishu, dingtalk, workwx
+ EnabledContactTypes []string `json:"enabled_contact_types"`
+
+ // description: verified contact types for user
+ // example: email, mobile, feishu, dingtalk, workwx
+ VerifiedContactTypes []string `json:"verified_contact_types"`
+}
+
+type ReceiverListInput struct {
+ apis.StatusStandaloneResourceListInput
+ apis.DomainizedResourceListInput
+ apis.EnabledResourceBaseListInput
+
+ UID string `json:"uid"`
+
+ UName string `json:"uname"`
+
+ EnabledContactType string `json:"enabled_contact_type"`
+
+ VerifiedContactType string `json:"verified_contact_type"`
+}
+
+type ReceiverUpdateInput struct {
+ apis.StatusStandaloneResourceBaseUpdateInput
+
+ // description: user email
+ // example: example@gmail.com
+ Email string `json:"email"`
+
+ // description: user mobile
+ // example: 17812345678
+ Mobile string `json:"mobile"`
+
+ // description: enabled contacts for user
+ // example: {"email", "mobile", "feishu", "dingtalk", "workwx"}
+ EnabledContactTypes []string `json:"enabled_contact_types"`
+}
+
+type ReceiverTriggerVerifyInput struct {
+ // description: contact type
+ // required: true
+ // example: email
+ // enum: email,mobile
+ ContactType string `json:"contact_type"`
+}
+
+type ReceiverVerifyInput struct {
+ // description: Contact type
+ // required: true
+ // example: email
+ // enum: email,mobile
+ ContactType string `json:"contact_type"`
+ // description: token user input
+ // required: true
+ // example: 123456
+ Token string `json:"token"`
+}
diff --git a/pkg/apis/notify/template.go b/pkg/apis/notify/template.go
new file mode 100644
index 0000000000..751d0f70ae
--- /dev/null
+++ b/pkg/apis/notify/template.go
@@ -0,0 +1,82 @@
+// Copyright 2019 Yunion
+//
+// 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 notify
+
+import "yunion.io/x/onecloud/pkg/apis"
+
+type TemplateCreateInput struct {
+ apis.StandaloneResourceCreateInput
+
+ // description: Contact type, specifically, setting it to all means all contact type
+ // require: true
+ // example: email
+ ContactType string `json:"contact_type"`
+ // description: Template type
+ // enum: title,content,remote
+ // example: title
+ TemplateType string `json:"template_type"`
+
+ // description: Template topic
+ // required: true
+ // example: IMAGE_ACTIVE
+ Topic string `json:"topic"`
+
+ // description: Template content
+ // required: true
+ // example: 镜像 {{.name}} 上传完成
+ Content string `json:"content"`
+ // description: Example for using this template
+ // required: true
+ // example: {"name": "centos7.6"}
+ Example string `json:"example"`
+}
+
+type TemplateListInput struct {
+ apis.StandaloneResourceListInput
+
+ // description: Contact type, specifically, setting it to all means all contact type
+ // require: true
+ // example: email
+ ContactType string `json:"contact_type"`
+
+ // description: Template type
+ // enum: title,content,remote
+ // example: title
+ TemplateType string `json:"template_type"`
+
+ // description: template topic
+ // required: true
+ // example: IMAGE_ACTIVE
+ Topic string `json:"topic"`
+}
+
+type TemplateUpdateInput struct {
+ apis.StandaloneResourceCreateInput
+
+ // description: template content
+ // required: true
+ // example: 镜像 {{.name}} 上传完成
+ Content string `json:"content"`
+ // description: all example for using this template
+ // required: true
+ // example: {"name": "centos7.6"}
+ Example string `json:"example"`
+}
+
+type TemplateDetails struct {
+ apis.StandaloneResourceDetails
+
+ STemplate
+}
diff --git a/pkg/apis/notify/zz_generated.model.go b/pkg/apis/notify/zz_generated.model.go
new file mode 100644
index 0000000000..fc90209557
--- /dev/null
+++ b/pkg/apis/notify/zz_generated.model.go
@@ -0,0 +1,101 @@
+// 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.
+
+// Code generated by model-api-gen. DO NOT EDIT.
+
+package notify
+
+import (
+ time "time"
+
+ "yunion.io/x/onecloud/pkg/apis"
+)
+
+// SConfig is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SConfig.
+type SConfig struct {
+ apis.SStandaloneResourceBase
+ Type string `json:"type"`
+ Content interface{} `json:"content"`
+}
+
+// SNotification is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SNotification.
+type SNotification struct {
+ apis.SStatusStandaloneResourceBase
+ ContactType string `json:"contact_type"`
+ // swagger:ignore
+ Topic string `json:"topic"`
+ Priority string `json:"priority"`
+ // swagger:ignore
+ Message string `json:"message"`
+ ReceivedAt time.Time `json:"received_at"`
+ SendTimes int `json:"send_times"`
+}
+
+// SReceiver is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SReceiver.
+type SReceiver struct {
+ apis.SStatusStandaloneResourceBase
+ apis.SDomainizedResourceBase
+ apis.SEnabledResourceBase
+ Email string `json:"email"`
+ Mobile string `json:"mobile"`
+ // swagger:ignore
+ EnabledEmail *bool `json:"enabled_email,omitempty"`
+ // swagger:ignore
+ VerifiedEmail *bool `json:"verified_email,omitempty"`
+ // swagger:ignore
+ EnabledMobile *bool `json:"enabled_mobile,omitempty"`
+ // swagger:ignore
+ VerifiedMobile *bool `json:"verified_mobile,omitempty"`
+}
+
+// SReceiverNotification is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SReceiverNotification.
+type SReceiverNotification struct {
+ apis.SJointResourceBase
+ RecevierID string `json:"recevier_id"`
+ NotificationID string `json:"notification_id"`
+ // ignore if RecevierID is not empty
+ Contact string `json:"contact"`
+ SendBy string `json:"send_by"`
+ Status string `json:"status"`
+ FailedReason string `json:"failed_reason"`
+}
+
+// SSubContact is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SSubContact.
+type SSubContact struct {
+ apis.SResourceBase
+ // id of receiver user
+ RecevierID string `json:"recevier_id"`
+ Type string `json:"type"`
+ Contact string `json:"contact"`
+ ParentContactType string `json:"parent_contact_type"`
+ Enabled *bool `json:"enabled,omitempty"`
+ Verified *bool `json:"verified,omitempty"`
+}
+
+// STemplate is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.STemplate.
+type STemplate struct {
+ apis.SStandaloneResourceBase
+ ContactType string `json:"contact_type"`
+ Topic string `json:"topic"`
+ // title | content | remote
+ TemplateType string `json:"template_type"`
+ Content string `json:"content"`
+ Example string `json:"example"`
+}
+
+// SVerification is an autogenerated struct via yunion.io/x/onecloud/pkg/notify/models.SVerification.
+type SVerification struct {
+ apis.SResourceBase
+ ReceiverId string `json:"receiver_id"`
+ ContactType string `json:"contact_type"`
+ Token string `json:"token"`
+}
diff --git a/pkg/mcclient/modules/managers.go b/pkg/mcclient/modules/managers.go
index 32f0a889d0..5cfa384336 100644
--- a/pkg/mcclient/modules/managers.go
+++ b/pkg/mcclient/modules/managers.go
@@ -74,6 +74,14 @@ func NewNotifyManager(keyword, keywordPlural string, columns, adminColumns []str
Keyword: keyword, KeywordPlural: keywordPlural}
}
+func NewNotifyv2Manager(keyword, keywordPlural string, columns, adminColumns []string) modulebase.ResourceManager {
+ return modulebase.ResourceManager{
+ BaseManager: *modulebase.NewBaseManager(apis.SERVICE_TYPE_NOTIFY, "", "v2", columns, adminColumns),
+ Keyword: keyword,
+ KeywordPlural: keywordPlural,
+ }
+}
+
func NewJointComputeManager(keyword, keywordPlural string, columns, adminColumns []string, master, slave modulebase.Manager) modulebase.JointResourceManager {
return modulebase.JointResourceManager{
ResourceManager: NewComputeManager(keyword, keywordPlural, columns, adminColumns),
diff --git a/pkg/mcclient/modules/mod_notify.go b/pkg/mcclient/modules/mod_notify.go
index 6552269f60..d348de9f6a 100644
--- a/pkg/mcclient/modules/mod_notify.go
+++ b/pkg/mcclient/modules/mod_notify.go
@@ -21,13 +21,43 @@ type ConfigsManager struct {
}
var (
- Configs ConfigsManager
+ NotifyReceiver modulebase.ResourceManager
+ NotifyConfig modulebase.ResourceManager
+ Notification modulebase.ResourceManager
+ NotifyTemplate modulebase.ResourceManager
+ Configs ConfigsManager
)
func init() {
- Configs = ConfigsManager{NewNotifyManager("config", "configs",
+ NotifyReceiver = NewNotifyv2Manager(
+ "receiver",
+ "receivers",
+ []string{"ID", "Name", "Email", "Mobile", "Enabled_Contact_Types", "Verified_Contact_Types"},
[]string{},
- []string{})}
+ )
+ register(&NotifyReceiver)
- register(&Configs)
+ NotifyConfig = NewNotifyv2Manager(
+ "notifyconfig",
+ "notifyconfigs",
+ []string{"Type", "Content"},
+ []string{},
+ )
+ register(&NotifyConfig)
+
+ Notification = NewNotifyv2Manager(
+ "notification",
+ "notifications",
+ []string{"Title", "Content", "ContactType", "Priority", "Receiver_Details"},
+ []string{},
+ )
+ register(&Notification)
+
+ NotifyTemplate = NewNotifyv2Manager(
+ "notifytemplate",
+ "notifytemplates",
+ []string{"ID", "Name", "Contact_Type", "Topic", "Template_Type", "Content", "Example"},
+ []string{},
+ )
+ register(&NotifyTemplate)
}
diff --git a/pkg/mcclient/modules/mod_notify_template.go b/pkg/mcclient/modules/mod_notify_template.go
deleted file mode 100644
index 42ad402635..0000000000
--- a/pkg/mcclient/modules/mod_notify_template.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 modules
-
-import (
- "yunion.io/x/jsonutils"
-
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/modulebase"
-)
-
-type NotifyTemplateManager struct {
- modulebase.ResourceManager
-}
-
-func (nm *NotifyTemplateManager) DeleteContents(s *mcclient.ClientSession, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- path := "/notifytemplates/delete-template"
- return modulebase.Post(nm.ResourceManager, s, path, params, "")
-}
-
-var (
- NotifyTemplates NotifyTemplateManager
-)
-
-func init() {
- NotifyTemplates = NotifyTemplateManager{NewNotifyManager("notifytemplate", "notifytemplates",
- []string{"Contact_Type", "Topic", "Template_Type"},
- []string{})}
-
- register(&NotifyTemplates)
-}
diff --git a/pkg/mcclient/modules/notify/mod_notification.go b/pkg/mcclient/modules/notify/mod_notification.go
index ecf98e79be..1ae0d2b37d 100644
--- a/pkg/mcclient/modules/notify/mod_notification.go
+++ b/pkg/mcclient/modules/notify/mod_notification.go
@@ -15,9 +15,9 @@
package notify
import (
- "fmt"
-
"yunion.io/x/jsonutils"
+ "yunion.io/x/pkg/errors"
+ "yunion.io/x/pkg/util/sets"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
@@ -39,26 +39,54 @@ type SNotifyMessage struct {
Broadcast bool `json:"broadcast,omitempty"`
}
+type SNotifyV2Message struct {
+ ReceiverIds []string `json:"receiver_ids"`
+ ContactType string `json:"contact_type"`
+ Topic string `json:"topic"`
+ Priority string `json:"priority"`
+ Message string `json:"message"`
+}
+
type NotificationManager struct {
modulebase.ResourceManager
}
func (manager *NotificationManager) Send(s *mcclient.ClientSession, msg SNotifyMessage) error {
- params := jsonutils.Marshal(&msg)
- body := jsonutils.NewDict()
- body.Add(params, manager.Keyword)
+ receiverIds := make([]string, 0, len(msg.Uid))
+ if len(msg.Gid) > 0 {
+ // fetch uid
+ uidSet := sets.NewString()
+ for _, gid := range msg.Gid {
+ users, err := modules.Groups.GetUsers(s, gid, nil)
+ if err != nil {
+ return errors.Wrapf(err, "Groups.GetUsers for group %q", gid)
+ }
+ for i := range users.Data {
+ id, _ := users.Data[i].GetString("id")
+ uidSet.Insert(id)
+ }
+ }
+ for _, uid := range uidSet.UnsortedList() {
+ receiverIds = append(receiverIds, uid)
+ }
+ }
+ receiverIds = append(receiverIds, msg.Uid...)
- path := fmt.Sprintf("/%s?uname=true", manager.ContextPath(nil))
- _, err := modulebase.Post(manager.ResourceManager, s, path, body, manager.KeywordPlural)
+ v2msg := SNotifyV2Message{
+ ReceiverIds: receiverIds,
+ ContactType: string(msg.ContactType),
+ Topic: msg.Topic,
+ Priority: string(msg.Priority),
+ Message: msg.Msg,
+ }
+ params := jsonutils.Marshal(&v2msg)
+
+ _, err := manager.Create(s, params)
return err
}
func init() {
Notifications = NotificationManager{
- modules.NewNotifyManager("notification", "notifications",
- []string{"id", "uid", "contact_type", "topic", "priority", "msg", "received_at", "send_by", "status", "create_at", "update_at", "delete_at", "create_by", "update_by", "delete_by", "is_deleted", "broadcast", "remark"},
- []string{}),
+ modules.Notification,
}
-
- modules.Register(&Notifications)
}
diff --git a/pkg/mcclient/modules/notify/mod_notification_test.go b/pkg/mcclient/modules/notify/mod_notification_test.go
deleted file mode 100644
index 4397e98a7c..0000000000
--- a/pkg/mcclient/modules/notify/mod_notification_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-// TODO: fix this test
-/*
-import (
- "testing"
-
- "yunion.io/x/jsonutils"
-)
-
-func TestNotificationManager(t *testing.T) {
- msg := SNotifyMessage{
- Uid: "testuser",
- ContactType: []TNotifyChannel{
- NotifyByEmail, NotifyByWebConsole,
- },
- Topic: "test message",
- Priority: NotifyPriorityNormal,
- Msg: "This is a test message. Yey!!",
- Remark: "Yunion",
- }
- msgJson := jsonutils.Marshal(msg)
- t.Logf("msg: %s", msgJson)
-}*/
diff --git a/pkg/notify/cache/user_group_cache.go b/pkg/notify/cache/user_group_cache.go
deleted file mode 100644
index 42886e416e..0000000000
--- a/pkg/notify/cache/user_group_cache.go
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 cache
-
-import (
- "context"
- "time"
-
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/compare"
-
- "yunion.io/x/onecloud/pkg/cloudcommon/consts"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
- "yunion.io/x/onecloud/pkg/mcclient/auth"
- "yunion.io/x/onecloud/pkg/mcclient/modules"
-)
-
-type SUserGroupCacheManager struct {
- db.SResourceBaseManager
-}
-
-type SUserGroup struct {
- db.SResourceBase
- UserId string
- GroupId string
- LastCheck time.Time `nullable:"false"`
-}
-
-func (ug *SUserGroup) GetModelManager() db.IModelManager {
- return UserGroupCacheManager
-}
-
-var UserGroupCacheManager *SUserGroupCacheManager
-
-func init() {
- UserGroupCacheManager = &SUserGroupCacheManager{db.NewResourceBaseManager(
- SUserGroup{},
- "user_group_cache_tbl",
- "usergroup",
- "usergroups",
- )}
-}
-
-func (ug *SUserGroup) IsExpired() bool {
- if ug.LastCheck.IsZero() {
- return true
- }
- now := time.Now().UTC()
- if ug.LastCheck.Add(consts.GetTenantCacheExpireSeconds()).Before(now) {
- return true
- }
- return false
-}
-
-func (manager *SUserGroupCacheManager) FetchByGroupId(ctx context.Context, groupId string) ([]SUserGroup, error) {
- q := manager.Query().Equals("gourp_id", groupId)
- ugs := make([]SUserGroup, 0)
- err := db.FetchModelObjects(manager, q, &ugs)
- if err != nil {
- return nil, err
- }
- var needSync bool
- if len(ugs) == 0 {
- needSync = true
- }
- now := time.Now().UTC()
- expireTime := now.Add(-consts.GetTenantCacheExpireSeconds())
- for i := range ugs {
- if ugs[i].LastCheck.Before(expireTime) {
- needSync = true
- break
- }
- }
- if !needSync {
- return ugs, nil
- }
- ugs, syncResult, err := manager.Sync(ctx, ugs, groupId)
- if err != nil {
- return nil, err
- }
- if syncResult.IsError() {
- log.Errorf(syncResult.Result())
- }
- return ugs, nil
-}
-
-func (manager *SUserGroupCacheManager) Sync(ctx context.Context, ugCache []SUserGroup, groupId string) ([]SUserGroup,
- compare.SyncResult, error) {
- lockman.LockRawObject(ctx, manager.KeywordPlural(), groupId)
- defer lockman.ReleaseRawObject(ctx, manager.KeywordPlural(), groupId)
-
- syncResult := compare.SyncResult{}
-
- // It's to query all groups and their users.
- query := jsonutils.NewDict()
- query.Set("scope", jsonutils.NewString("system"))
- query.Set("system", jsonutils.JSONTrue)
-
- s := auth.GetAdminSession(ctx, consts.GetRegion(), "v3")
- users, err := modules.Groups.GetUsers(s, groupId, query)
- if err != nil {
- return nil, syncResult, errors.Wrap(err, "fetch users by group id from keystone failed")
- }
- newUgCache := make([]SUserGroup, len(users.Data))
- for i := range users.Data {
- userId, _ := users.Data[i].GetString("id")
- newUgCache[i] = SUserGroup{
- UserId: userId,
- GroupId: groupId,
- }
- }
- added := make([]SUserGroup, 0)
- removed := make([]SUserGroup, 0)
- commondb := make([]SUserGroup, 0)
- compareSets(ugCache, newUgCache, &added, &removed, &commondb)
- now := time.Now().UTC()
- for i := range added {
- added[i].LastCheck = now
- err := manager.TableSpec().Insert(ctx, &added[i])
- if err != nil {
- syncResult.AddError(err)
- } else {
- syncResult.Add()
- }
- }
-
- for i := range commondb {
- ug := &commondb[i]
- _, err := db.Update(ug, func() error {
- ug.LastCheck = now
- return nil
- })
- if err != nil {
- syncResult.UpdateError(err)
- } else {
- syncResult.Update()
- }
- }
-
- for i := range removed {
- ug := &removed[i]
- _, err := db.Update(ug, func() error {
- return ug.MarkDelete()
- })
- if err != nil {
- syncResult.DeleteError(err)
- } else {
- syncResult.Delete()
- }
- }
-
- return newUgCache, syncResult, nil
-}
-
-func compareSets(dbs, remotes []SUserGroup, added, removed, commondb *[]SUserGroup) {
- dbmap := make(map[string]SUserGroup)
- for i := range dbs {
- dbmap[dbs[i].UserId] = dbs[i]
- }
-
- for i := range remotes {
- userId := remotes[i].UserId
- if _, ok := dbmap[userId]; ok {
- *commondb = append(*commondb, remotes[i])
- } else {
- *added = append(*added, remotes[i])
- }
- delete(dbmap, userId)
- }
- for _, v := range dbmap {
- *removed = append(*removed, v)
- }
-}
diff --git a/pkg/notify/compatible_config.go b/pkg/notify/compatible_config.go
deleted file mode 100644
index b2a40c00a2..0000000000
--- a/pkg/notify/compatible_config.go
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import (
- "context"
- "net/http"
-
- "yunion.io/x/jsonutils"
-
- "yunion.io/x/onecloud/pkg/appsrv"
- "yunion.io/x/onecloud/pkg/httperrors"
-)
-
-const (
- EMAIL_KEYWORDPLURAL = "email_configs"
- EMAIL_KEYWORD = "email_config"
- EMAIL = "email"
-
- SMS_KEYWORDPLURAL = "sms_configs"
- SMS_KEYWORD = "sms_config"
- SMS = "mobile"
-)
-
-func emailConfigDeleteHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- // do not need modify
- manager, _, _, _ := fetchEnv(ctx, w, r)
- params := map[string]string{
- "": EMAIL,
- }
- err := manager.DeleteConfig(ctx, params)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- }
-}
-
-func emailConfigGetHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, _, query, _ := fetchEnv(ctx, w, r)
- params := map[string]string{
- "": EMAIL,
- }
- ret, err := manager.GetConfig(ctx, params, query)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- }
- data, _ := ret.Get("config")
- dataDict := data.(*jsonutils.JSONDict)
- output := jsonutils.NewDict()
- output.Add(dataDict, EMAIL_KEYWORD)
- appsrv.SendJSON(w, output)
-}
-
-func emailConfigUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, _, _, body := fetchEnv(ctx, w, r)
- body, _ = body.Get(EMAIL_KEYWORD)
- bodyRet := jsonutils.DeepCopy(body)
- newBody := jsonutils.NewDict()
- newBody.Add(body, EMAIL)
- err := manager.UpdateConfig(ctx, newBody)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- }
- data := jsonutils.NewDict()
- data.Add(jsonutils.NewInt(200), "code")
- data.Add(jsonutils.NewString("OK"), "title")
- data.Add(bodyRet, "message")
- ret := jsonutils.NewDict()
- ret.Add(data, EMAIL_KEYWORD)
- appsrv.SendJSON(w, ret)
-}
-
-func smsConfigDeleteHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- // do not need modify
- manager, _, _, _ := fetchEnv(ctx, w, r)
- params := map[string]string{
- "": SMS,
- }
- err := manager.DeleteConfig(ctx, params)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- }
-}
-
-func smsConfigGetHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, _, query, _ := fetchEnv(ctx, w, r)
- params := map[string]string{
- "": SMS,
- }
- ret, err := manager.GetConfig(ctx, params, query)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- }
- // modify
- data, _ := ret.Get("config")
- appsrv.SendJSON(w, jsonutils.Marshal(map[string]jsonutils.JSONObject{
- SMS_KEYWORD: data,
- }))
-}
-
-func smsConfigUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, _, _, body := fetchEnv(ctx, w, r)
- body, _ = body.Get(SMS_KEYWORD)
- newBody := jsonutils.NewDict()
- newBody.Add(body, SMS)
- err := manager.UpdateConfig(ctx, newBody)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- }
- data := jsonutils.NewDict()
- data.Add(jsonutils.NewInt(200), "code")
- data.Add(jsonutils.NewString("OK"), "title")
- data.Add(body, "message")
- ret := jsonutils.NewDict()
- ret.Add(data, SMS_KEYWORD)
- appsrv.SendJSON(w, ret)
-}
diff --git a/pkg/notify/dispatcher.go b/pkg/notify/dispatcher.go
deleted file mode 100644
index 03d7e23451..0000000000
--- a/pkg/notify/dispatcher.go
+++ /dev/null
@@ -1,774 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import (
- "context"
- "fmt"
- "net/http"
- "strings"
- "time"
-
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/sets"
- "yunion.io/x/pkg/utils"
- "yunion.io/x/sqlchemy"
-
- "yunion.io/x/onecloud/pkg/appctx"
- "yunion.io/x/onecloud/pkg/appsrv"
- "yunion.io/x/onecloud/pkg/appsrv/dispatcher"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
- "yunion.io/x/onecloud/pkg/cloudcommon/policy"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/notify/models"
- noutils "yunion.io/x/onecloud/pkg/notify/utils"
- "yunion.io/x/onecloud/pkg/util/rbacutils"
-)
-
-// NotifyModelDispatcher is designed to complete some function that db.DBModelDispatcher can't.
-// The apis of notify module has a certain degree of particularity so that we can't use common function.
-type NotifyModelDispatcher struct {
- db.DBModelDispatcher
-}
-
-func NewNotifyModelDispatcher(manager db.IModelManager) *NotifyModelDispatcher {
- return &NotifyModelDispatcher{*db.NewModelHandler(manager)}
-}
-
-func (self *NotifyModelDispatcher) GetConfig(ctx context.Context, params map[string]string, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- listResult, err := self.List(ctx, mergeQueryParams(params, query), nil)
- if err != nil {
- return nil, err
- }
- configs := jsonutils.NewDict()
- for _, data := range listResult.Data {
- key, _ := data.GetString("key_text")
- value, _ := data.Get("value_text")
- configs.Add(value, key)
- }
- cType, ok := params[""]
- if ok {
- configs = models.ConfigManager.Database2Display(cType, configs)
- }
- output := jsonutils.NewDict()
- output.Add(configs, models.ConfigManager.Keyword())
- return output, nil
-}
-
-func (self *NotifyModelDispatcher) DeleteConfig(ctx context.Context, params map[string]string) error {
- contactType := params[""]
- configs, err := models.ConfigManager.GetConfigByType(contactType)
- if err != nil {
- return errors.Wrap(err, "Get Config by contactType failed")
- }
- userCred := policy.FetchUserCredential(ctx)
- for i := range configs {
- err = DeleteItem(&configs[i], ctx, userCred, jsonutils.JSONNull, jsonutils.JSONNull)
- if err != nil {
- return errors.Wrapf(err, "Delete part of old one, so please input new data again.")
- }
- }
- return nil
-}
-
-// UpdateConfig update config and restart corresponding send service.
-func (self *NotifyModelDispatcher) UpdateConfig(ctx context.Context, body jsonutils.JSONObject) error {
- data := body.(*jsonutils.JSONDict)
- contactType := data.SortedKeys()[0]
- originData, err := models.ConfigManager.GetConfig(contactType)
- if err != nil {
- return err
- }
- tmp, _ := data.Get(contactType)
- data = tmp.(*jsonutils.JSONDict)
- data = models.ConfigManager.Display2Database(contactType, data)
- userCred := policy.FetchUserCredential(ctx)
- // If no config of type 'contactType' in database, create news.
- // Else delete original ones and create news.
- if len(originData) != 0 {
- // delete original
- configs, err := models.ConfigManager.GetConfigByType(contactType)
- if err != nil {
- return errors.Wrap(err, "Get Config by contactType failed")
- }
- for i := range configs {
- err = DeleteItem(&configs[i], ctx, userCred, jsonutils.JSONNull, jsonutils.JSONNull)
- if err != nil {
- return errors.Wrapf(err, "Delete part of old one, so please input new data again.")
- }
- }
- }
- log.Debugf("update body: %s", data)
- keys := data.SortedKeys()
- config := make(map[string]string)
- createDataList := make([]jsonutils.JSONObject, 0, len(keys))
-
- // Extract data
- for _, key := range keys {
- createData := jsonutils.NewDict()
- tmp, _ = data.Get(key)
- createData.Add(tmp, "value_text")
- createData.Add(jsonutils.NewString(key), "key_text")
- createData.Add(jsonutils.NewString(contactType), "type")
- createDataList = append(createDataList, createData)
- value, _ := tmp.GetString()
- config[key] = value
- }
-
- // validate configs
- log.Debugf("config: %#v", config)
- isValid, message, err := models.NotifyService.ValidateConfig(ctx, contactType, config)
- if err != nil {
- if errors.Cause(err) != errors.ErrNotImplemented {
- return httperrors.NewInternalServerError("Validate Config error: %s", err.Error())
- }
- isValid = true
- }
- if !isValid {
- return httperrors.NewInputParameterError("validate failed: %s", message)
- }
-
- // create
- for _, createData := range createDataList {
- _, err := self.Create(ctx, jsonutils.NewDict(), createData, nil)
- if err != nil {
- return errors.Wrapf(err, "Create config %s for contact type %s failed", createData.String(), contactType)
- }
- }
-
- // update config
- models.RestartService(config, contactType)
- return nil
-}
-
-func (self *NotifyModelDispatcher) ValidateConfig(ctx context.Context, contactType string, body jsonutils.JSONObject) error {
- dict, ok := body.(*jsonutils.JSONDict)
- if !ok {
- return httperrors.NewInputParameterError("")
- }
- dict = models.ConfigManager.Display2Database(contactType, dict)
- configs := make(map[string]string)
- for _, key := range dict.SortedKeys() {
- value, err := dict.GetString(key)
- if err != nil {
- return errors.Wrap(err, "jsonutils.JsonDict.GetString")
- }
- configs[key] = value
- }
- isValid, message, err := models.NotifyService.ValidateConfig(ctx, contactType, configs)
- if err != nil {
- if errors.Cause(err) == errors.ErrNotImplemented {
- return httperrors.NewNotImplementedError("validating config of %s", contactType)
- }
- return err
- }
- if isValid == false {
- return httperrors.NewInputParameterError(message)
- }
- return nil
-}
-
-// CreateNotification create new notifications and send them through rpc.RpcService.
-// If data contains 'gid' field, that means that send message to all users in group.
-// Else send messager to user whose uid equals 'uid' in data.
-func (self *NotifyModelDispatcher) CreateNotification(ctx context.Context, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- // Get all contacts info of group if data contains "gid".
- // If no contact, return ErrContactNotFound.
- contactType, _ := data.GetString("contact_type")
- group := false
- var ids []string
- if data.Contains("gid") {
- group = true
- ids = self.getIds(data, "gid")
- } else {
- ids = self.getIds(data, "uid")
- }
- contacts, err := models.ContactManager.GetAllNotify(ctx, ids, contactType, group)
- if err != nil {
- return nil, httperrors.NewGeneralError(errors.Wrap(err, "get all contacts error"))
- }
-
- notificationIDs, err := models.NotificationManager.BatchCreate(ctx, data, contacts)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- ret := jsonutils.NewDict()
- ret.Add(jsonutils.NewStringArray(notificationIDs), "notifications")
- return ret, nil
-}
-
-func (self *NotifyModelDispatcher) getIds(data jsonutils.JSONObject, key string) []string {
- var ids []string
- tmpIds, err := data.GetArray(key)
- if err != nil {
- id, _ := data.GetString(key)
- ids = make([]string, 1)
- ids[0] = id
- } else {
- ids = noutils.JsonArrayToStringArray(tmpIds)
- }
- // remove entry in which empty content
- ret := make([]string, 0, len(ids))
- for _, id := range ids {
- if len(id) == 0 {
- continue
- }
- ret = append(ret, id)
- }
- return ret
-}
-
-// Verify process:
-// 1.fetch verify by ID; 2.check that if verify is expired;
-// 3.if not check that if token is correct and update status of contact whose id is verify's CID
-// 4.otherwise generate a new verify and delete old one
-func (self *NotifyModelDispatcher) Verify(ctx context.Context, params map[string]string, query jsonutils.JSONObject) error {
- processID := params[""]
- token, _ := query.GetString("token")
- manager := models.VerifyManager
- verifys, err := manager.FetchByID(processID)
- if err != nil {
- return httperrors.NewGeneralError(err)
- }
- var verifition models.SVerify
- if len(verifys) == 0 {
- return httperrors.NewNotFoundError("%s verify record not found", processID)
- }
- current, have := time.Now(), false
- for i := range verifys {
- if current.Before(verifys[i].ExpireAt) {
- verifition = verifys[i]
- have = true
- break
- }
- }
- if !have {
- return httperrors.NewBadRequestError(models.VERIFICATION_TOKEN_EXPIRED)
- }
- if verifition.Token != token {
- return httperrors.NewBadRequestError(models.VERIFICATION_TOKEN_INVALID)
- }
- // modify contact's status and verified time.
- data := jsonutils.NewDict()
- data.Set("status", jsonutils.NewString(models.VERIFICATION_VERIFIED))
- data.Set("verified_at", jsonutils.NewTimeString(current))
- _, err = self.Update(ctx, verifition.CID, jsonutils.NewDict(), data, nil)
- if err != nil {
- return httperrors.NewGeneralError(err)
- }
- return nil
-}
-
-// VerifyTrigger process:
-// 1.fetch contact by the information in data
-// 2.if contact'status is 'init', make a new verify and send a verify message to the contact adress
-// 3.if contact'status is 'verifying', fetch verify by CID, generate a new verify if it has expired
-// or return a error mention that "please don't try again".
-func (self *NotifyModelDispatcher) VerifyTrigger(ctx context.Context, params map[string]string, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
- uid := params[""]
- contact, _ := data.GetString("contact")
- contactType, _ := data.GetString("contact_type")
- contacts, err := models.ContactManager.FetchByMore(uid, contact, contactType)
- if err != nil || len(contacts) == 0 {
- return nil, errors.Error(fmt.Sprintf("uid '%s' don't have contact '%s' of contact_type '%s'", uid, contact, contactType))
- }
- userCred := policy.FetchUserCredential(ctx)
- scontact := contacts[0]
-
- makeNewVerify := func() (jsonutils.JSONObject, error) {
- verification := models.NewSVerify(contactType, scontact.ID)
- err = models.VerifyManager.Create(ctx, userCred, verification)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- // update contact state
- if scontact.Status != models.CONTACT_VERIFYING {
- scontact.SetStatus(userCred, models.CONTACT_VERIFYING, "")
- }
-
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- processID := verification.ID
- err := models.SendVerifyMessage(ctx, userCred, verification, &scontact)
- if err != nil {
- return nil, err
- }
- ret := map[string]map[string]string{
- "contact": {
- "process_id": processID,
- },
- }
- return jsonutils.Marshal(ret), nil
- }
- if scontact.Status == models.CONTACT_INIT {
- return makeNewVerify()
- }
- if scontact.Status == models.CONTACT_VERIFYING {
- verifications, err := models.VerifyManager.FetchByCID(scontact.ID, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
- q = q.In("status", []string{models.VERIFICATION_SENT, "init"}).Desc("created_at")
- return q
- })
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- if len(verifications) == 0 {
- // no verifications in status "sent"
- return makeNewVerify()
- }
- current := time.Now()
- for _, verification := range verifications {
- if current.After(verification.ExpireAt) {
- //delete old one
- err = DeleteItem(&verification, ctx, userCred, jsonutils.JSONNull, jsonutils.JSONNull)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- return makeNewVerify()
- }
- }
- return nil, httperrors.NewGeneralError(models.ErrVeritying)
- }
-
- return jsonutils.JSONNull, nil
-}
-
-// DeleteContacts delete a group of contacts
-func (self *NotifyModelDispatcher) DeleteContacts(ctx context.Context, uidArray []jsonutils.JSONObject) error {
- // Get all id of uid
- uids := make([]string, len(uidArray))
- log.Debugf("uidArray: %s", uidArray)
- for i := range uidArray {
- uids[i], _ = uidArray[i].GetString()
- }
- log.Debugf("uids: %#v", uids)
- uname := false
- if v := ctx.Value("uname"); v != nil {
- uname = true
- }
- contacts, err := models.ContactManager.FetchByUIDs(ctx, uids, uname)
- if err != nil {
- return httperrors.NewGeneralError(err)
- }
- userCred := policy.FetchUserCredential(ctx)
- deleteFailed := make([]string, 0, 1)
- for _, contact := range contacts {
- err = DeleteItem(&contact, ctx, userCred, jsonutils.JSONNull, jsonutils.JSONNull)
- if err != nil {
- deleteFailed = append(deleteFailed, contact.ID)
- }
- }
- // clean cache
- noutils.DeleteUsers(ctx, userCred, uids)
- if len(deleteFailed) != 0 {
- errInfo := strings.Join(deleteFailed, ", ") + " ; these contact delete failed."
- return errors.Error(errInfo)
- }
- return nil
-}
-
-// 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, pullCtypes []jsonutils.JSONObject, ctxIds []dispatcher.SResourceContext) (jsonutils.JSONObject,
- error) {
-
- type pair struct {
- contact string
- enabled string
- }
-
- // contactInfos will be used to find all contact info need to update.
- // And others will be created.
- contactInfos := make(map[string]pair)
- contactTypes := make([]string, len(datas))
- for i := range datas {
- contactType, _ := datas[i].GetString("contact_type")
- if _, ok := models.UpdateNotAllow[contactType]; ok {
- continue
- }
- contact, _ := datas[i].GetString("contact")
- enabled := "-1"
- if datas[i].Contains("enabled") {
- enabled, _ = datas[i].GetString("enabled")
- }
- contactInfos[contactType] = pair{contact, enabled}
- contactTypes[i] = contactType
- }
-
- records, err := models.ContactManager.FetchByUIDAndCType(idstr, contactTypes)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
-
- // updateFailed record the information of failed update
- updateFailed := make([]string, 0, 1)
- deleteFailed := make([]string, 0, 1)
- // UpdateItem contact info
- userCred := policy.FetchUserCredential(ctx)
- for i := range records {
- contactType := records[i].ContactType
- pairUpdate := contactInfos[contactType]
- if len(pairUpdate.contact) == 0 {
- // delete
- err = DeleteItem(&records[i], ctx, userCred, jsonutils.JSONNull, jsonutils.JSONNull)
- if err != nil {
- deleteFailed = append(deleteFailed, fmt.Sprintf(`uid:%q, contact_type:%q`, idstr, contactType))
- }
- continue
- }
- updateData := jsonutils.NewDict()
- updateData.Set("contact", jsonutils.NewString(pairUpdate.contact))
- if pairUpdate.enabled != "-1" {
- updateData.Set("enabled", jsonutils.NewString(pairUpdate.enabled))
- }
- if records[i].Contact != pairUpdate.contact {
- updateData.Set("status", jsonutils.NewString(models.CONTACT_INIT))
- }
- // update is not relational
- //updateData.Set("status", jsonutils.NewString("init"))
- err = UpdateItem(models.ContactManager, &records[i], ctx, userCred, jsonutils.JSONNull, updateData)
- if err != nil {
- updateFailed = append(updateFailed, fmt.Sprintf(`uid:%q, contact_type:%q, contact:%q`, idstr, contactType, pairUpdate.contact))
- }
- delete(contactInfos, contactType)
- }
-
- // createFailed record the information of failed creation
- createFailed := make([]string, 0, 1)
- // Create contact info
- type CreateData struct {
- UID string
- ContactType string
- Contact string
- Enabled string
- }
- newDatas := make([]CreateData, 0, len(contactInfos))
- for conType, conPair := range contactInfos {
- tmp := CreateData{
- UID: idstr,
- ContactType: conType,
- Contact: conPair.contact,
- }
- if conPair.enabled != "-1" {
- tmp.Enabled = conPair.enabled
- }
- newDatas = append(newDatas, tmp)
- }
-
- // Enable closing feishu and dingtalk notify.
- // Temporary solution for 3.2.
- allPullType := sets.NewString(models.FEISHU, models.DINGTALK)
-
- pulls := make([]string, len(pullCtypes))
- for i := range pullCtypes {
- pulls[i], _ = pullCtypes[i].GetString()
- allPullType.Delete(pulls[i])
- }
-
- // delete first
- records, err = models.ContactManager.FetchByUIDAndCType(idstr, allPullType.UnsortedList())
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- for i := range records {
- err := DeleteItem(&records[i], ctx, userCred, jsonutils.JSONNull, jsonutils.JSONNull)
- if err != nil {
- deleteFailed = append(deleteFailed, fmt.Sprintf(`uid:%q, contact_type:%q`, idstr, records[i].ContactType))
- }
- }
-
- if len(pulls) > 0 {
- contacts, err := models.ContactManager.FetchByUIDAndCType(idstr, pulls)
- if err != nil {
- return nil, err
- }
- set := sets.NewString(pulls...)
- for i := range contacts {
- set.Delete(contacts[i].ContactType)
- }
- for _, ct := range set.UnsortedList() {
- tmp := CreateData{
- UID: idstr,
- ContactType: ct,
- Enabled: "1",
- Contact: "user_id",
- }
- newDatas = append(newDatas, tmp)
- }
- }
- log.Debugf("newDatas: %s", newDatas)
-
- for _, newData := range newDatas {
- _, err := self.Create(ctx, jsonutils.NewDict(), jsonutils.Marshal(newData), ctxIds)
- if err != nil {
- createFailed = append(createFailed, fmt.Sprintf(`uid:%q, contact_type:%q, contact:%q`, idstr,
- newData.ContactType, newData.Contact))
- }
- }
-
- // generate error through updateFailed and createFailed
- if len(updateFailed) != 0 || len(createFailed) != 0 || len(deleteFailed) != 0 {
- var errInfoBuffer strings.Builder
- if len(updateFailed) != 0 {
- errInfoBuffer.WriteString(strings.Join(updateFailed, "; "))
- errInfoBuffer.WriteString(" update failed. ")
- }
- if len(deleteFailed) != 0 {
- errInfoBuffer.WriteString(strings.Join(updateFailed, "; "))
- errInfoBuffer.WriteString(" delete failed. ")
- }
- if len(createFailed) != 0 {
- errInfoBuffer.WriteString(strings.Join(createFailed, "; "))
- errInfoBuffer.WriteString(" create failed. ")
- }
- errInfo := errInfoBuffer.String()
- return nil, httperrors.NewGeneralError(errors.Error(errInfo))
- }
-
- models.PullContact(idstr, pulls)
-
- // keep the return value same as this of the GET interface
- ret := jsonutils.NewDict()
- contacts, err := models.ContactManager.FetchByUIDs(ctx, []string{idstr}, false)
- if err != nil {
- log.Errorf("fetch contact %s: %v", idstr, err)
- return ret, nil
- }
- if len(contacts) == 0 {
- return nil, nil
- }
- contact := contacts[0]
- outDetails, err := contact.GetExtraDetails(ctx, userCred, ret, false)
- if err != nil {
- log.Errorf("fetch contact details %s(%s): %v",
- contact.GetName(), contact.GetId(), err)
- return ret, nil
- }
- out := jsonutils.Marshal(outDetails)
- out.(*jsonutils.JSONDict).Set("created_at", jsonutils.NewString(contact.CreatedAt.String()))
- return out, nil
-}
-
-func (self *NotifyModelDispatcher) UpdateTemplate(ctx context.Context, ctype string, query jsonutils.JSONObject,
- datas []jsonutils.JSONObject) error {
-
- type sTemplate struct {
- ContactType string
- Topic string
- TemplateType string
- Content string
- }
- templates := make([]sTemplate, 0, len(datas))
- topics := sets.NewString()
- for _, data := range datas {
- var tem sTemplate
- err := data.Unmarshal(&tem)
- if err != nil {
- return errors.Wrap(err, "data.Unmarshal")
- }
- if tem.TemplateType != models.TEMPLATE_TYPE_REMOTE && tem.TemplateType != models.
- TEMPLATE_TYPE_CONTENT && tem.TemplateType != models.TEMPLATE_TYPE_TITLE {
-
- return httperrors.NewInputParameterError("no support for such template type '%s'", tem.TemplateType)
- }
- tem.ContactType = ctype
- tem.Topic = strings.ToUpper(tem.Topic)
- templates = append(templates, tem)
- topics.Insert(tem.Topic)
- }
-
- q := models.TemplateManager.Query().Equals("contact_type", ctype).In("topic", topics.List())
- templateModels := make([]models.STemplate, 0, 1)
- err := db.FetchModelObjects(models.TemplateManager, q, &templateModels)
- if err != nil {
- log.Errorf("db.FetchModelObjects sql: %s", q.String())
- return errors.Wrap(err, "db.FetchModelObjects")
- }
-
- templateMaps := make(map[string]*models.STemplate)
- for i := range templateModels {
- k := fmt.Sprintf("%s/%s/%s", templateModels[i].ContactType, templateModels[i].Topic, templateModels[i].TemplateType)
- templateMaps[k] = &templateModels[i]
- }
-
- userCred := policy.FetchUserCredential(ctx)
- for _, tem := range templates {
- k := fmt.Sprintf("%s/%s/%s", tem.ContactType, tem.Topic, tem.TemplateType)
- if tmod, ok := templateMaps[k]; ok {
- updateData := jsonutils.NewDict()
- updateData.Add(jsonutils.NewString(tem.Content), "content")
- err = UpdateItem(models.TemplateManager, tmod, ctx, userCred, query, updateData)
- if err != nil {
- return errors.Wrapf(err, "fail to update template '%s'", tmod.ID)
- }
- continue
- }
- _, err = self.Create(ctx, query, jsonutils.Marshal(tem), nil)
- if err != nil {
- return errors.Wrapf(err, "fail to create template(contact_type: %s, topic: %s, template_type: %s)",
- tem.ContactType, tem.Topic, tem.TemplateType)
- }
- }
- return nil
-}
-
-func (self *NotifyModelDispatcher) DeleteTemplate(ctx context.Context, query jsonutils.JSONObject, ctype, topic string) error {
-
- q := models.TemplateManager.Query().Equals("contact_type", ctype)
- if len(topic) != 0 {
- q = q.Equals("topic", topic)
- }
- templates := make([]models.STemplate, 0, 1)
- err := db.FetchModelObjects(models.TemplateManager, q, &templates)
- if err != nil {
- log.Errorf("db.FetchModelObjects sql: %s", q.String())
- return errors.Wrap(err, "db.FetchModelObjects")
- }
- userCred := policy.FetchUserCredential(ctx)
- for i := range templates {
- err = DeleteItem(&templates[i], ctx, userCred, query, jsonutils.JSONNull)
- if err != nil {
- return errors.Wrapf(err, "fail to delete template '%s'", templates[i].ID)
- }
- }
- return nil
-}
-
-// fetchEnv fetch handler, params, query and body from ctx(context.Context)
-func fetchEnv(ctx context.Context, w http.ResponseWriter, r *http.Request) (*NotifyModelDispatcher, map[string]string, jsonutils.JSONObject, jsonutils.JSONObject) {
- params, query, body := appsrv.FetchEnv(ctx, w, r)
- metadata := appctx.AppContextMetadata(ctx)
- manager, ok := metadata["manager"].(*NotifyModelDispatcher)
- if !ok {
- log.Fatalf("No manager found for URL: %s", r.URL)
- }
- return manager, params, query, body
-}
-
-func mergeQueryParams(params map[string]string, query jsonutils.JSONObject, excludes ...string) jsonutils.JSONObject {
- if query == nil {
- query = jsonutils.NewDict()
- }
- queryDict := query.(*jsonutils.JSONDict)
- for k, v := range params {
- if !utils.IsInStringArray(k, excludes) {
- queryDict.Add(jsonutils.NewString(v), k[1:len(k)-1])
- }
- }
- return queryDict
-}
-
-// DeleteItem delete a database record corresponding to model
-func DeleteItem(model db.IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
- lockman.LockObject(ctx, model)
- defer lockman.ReleaseObject(ctx, model)
- err := model.ValidateDeleteCondition(ctx)
- if err != nil {
- log.Errorf("validate delete condition error: %s", err)
- return err
- }
- err = db.CustomizeDelete(model, ctx, userCred, query, data)
- if err != nil {
- log.Errorf("customize delete error: %s", err)
- return httperrors.NewNotAcceptableError("%v", err)
- }
- model.PreDelete(ctx, userCred)
- err = model.Delete(ctx, userCred)
- if err != nil {
- log.Errorf("Delete error %s", err)
- return err
- }
- model.PostDelete(ctx, userCred)
- return nil
-}
-
-// UpdateItem update a database record corresponding to model whose update fields are in data
-func UpdateItem(manager db.IModelManager, item db.IModel, ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
- lockman.LockObject(ctx, item)
- defer lockman.ReleaseObject(ctx, item)
- var err error
- err = item.ValidateUpdateCondition(ctx)
-
- if err != nil {
- log.Errorf("validate update condition error: %s", err)
- return httperrors.NewGeneralError(err)
- }
-
- dataDict, ok := data.(*jsonutils.JSONDict)
- if !ok {
- return httperrors.NewInternalServerError("Invalid data JSONObject")
- }
-
- dataDict, err = db.ValidateUpdateData(item, ctx, userCred, query, dataDict)
- if err != nil {
- errMsg := fmt.Sprintf("validate update data error: %s", err)
- log.Errorf(errMsg)
- return httperrors.NewGeneralError(err)
- }
-
- item.PreUpdate(ctx, userCred, query, dataDict)
-
- diff, err := db.Update(item, func() error {
- filterData := dataDict.CopyIncludes(updateFields(manager, userCred)...)
- err = filterData.Unmarshal(item)
- if err != nil {
- errMsg := fmt.Sprintf("unmarshal fail: %s", err)
- log.Errorf(errMsg)
- return httperrors.NewGeneralError(err)
- }
- return nil
- })
-
- if err != nil {
- log.Errorf("save update error: %s", err)
- return httperrors.NewGeneralError(err)
- }
- db.OpsLog.LogEvent(item, db.ACT_UPDATE, diff, userCred)
-
- item.PostUpdate(ctx, userCred, query, data)
-
- return nil
-
-}
-
-// get the field of model which is d
-func updateFields(manager db.IModelManager, userCred mcclient.TokenCredential) []string {
- ret := make([]string, 0)
- for _, col := range manager.TableSpec().Columns() {
- tags := col.Tags()
- update := tags["update"]
- if allowAction(manager, userCred, update, db.IsAllowUpdate) {
- ret = append(ret, col.Name())
- }
- }
- return ret
-}
-
-func allowAction(manager db.IResource, userCred mcclient.TokenCredential, action string, testfunc func(scope rbacutils.TRbacScope, userCred mcclient.TokenCredential, manager db.IResource) bool) bool {
- if action == "user" {
- return true
- }
- if action == "domain" && (testfunc(rbacutils.ScopeDomain, userCred, manager) || testfunc(rbacutils.ScopeSystem, userCred, manager)) {
- return true
- }
- if action == "admin" && testfunc(rbacutils.ScopeSystem, userCred, manager) {
- return true
- }
- return false
-}
diff --git a/pkg/notify/handlers.go b/pkg/notify/handlers.go
deleted file mode 100644
index 71485c9b4c..0000000000
--- a/pkg/notify/handlers.go
+++ /dev/null
@@ -1,450 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 notify
-
-import (
- "context"
- "fmt"
- "net/http"
-
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
-
- "yunion.io/x/onecloud/pkg/appctx"
- "yunion.io/x/onecloud/pkg/appsrv"
- "yunion.io/x/onecloud/pkg/cloudcommon/consts"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/policy"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient/auth"
- "yunion.io/x/onecloud/pkg/mcclient/modulebase"
- "yunion.io/x/onecloud/pkg/mcclient/modules"
- "yunion.io/x/onecloud/pkg/notify/cache"
- "yunion.io/x/onecloud/pkg/notify/models"
- "yunion.io/x/onecloud/pkg/notify/options"
- "yunion.io/x/onecloud/pkg/notify/utils"
-)
-
-var API_VERSION = "api/v1"
-
-func InitHandlers(app *appsrv.Application) {
- // add version handler with API_VERSION prefix
- app.AddDefaultHandler("GET", API_VERSION+"/version", appsrv.VersionHandler, "version")
- db.AddScopeResourceCountHandler(API_VERSION, app)
-
- db.RegisterModelManager(models.ContactManager)
- db.RegisterModelManager(models.VerifyManager)
- db.RegisterModelManager(models.NotificationManager)
- db.RegisterModelManager(models.ConfigManager)
- db.RegisterModelManager(cache.UserCacheManager)
- db.RegisterModelManager(cache.UserGroupCacheManager)
- db.RegisterModelManager(models.TemplateManager)
- AddNotifyDispatcher(API_VERSION, app)
-}
-
-// Middleware
-func middleware(f appsrv.FilterHandler) appsrv.FilterHandler {
- hander := func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- if _, ok := r.URL.Query()["uname"]; ok {
- // Uname
- log.Debugf("url.Query has a uname")
- params := appctx.AppContextParams(ctx)
- if uid, ok := params[""]; ok {
- userDetail, err := utils.GetUserByIDOrName(ctx, uid)
- if err != nil {
- httperrors.NotFoundError(ctx, w, "Uid or Uname Not Found")
- return
- }
- log.Debugf("find userDetail, id: %s, name: %s", userDetail.Id, userDetail.Name)
- params[""] = userDetail.Id
- }
- ctx = context.WithValue(ctx, "uname", true)
- }
- f(ctx, w, r)
- }
- if consts.IsRbacEnabled() {
- return auth.AuthenticateWithDelayDecision(hander, true)
- } else {
- return auth.Authenticate(hander)
- }
-}
-
-func AddNotifyDispatcher(prefix string, app *appsrv.Application) {
- var metadata map[string]interface{}
- var tags map[string]string
-
- // Contact Handler
- modelDispatcher := NewNotifyModelDispatcher(models.ContactManager)
- metadata, tags = map[string]interface{}{"manager": modelDispatcher}, map[string]string{"resource": modelDispatcher.KeywordPlural()}
- app.AddHandler2("POST",
- fmt.Sprintf("%s/%s//update-contact", prefix, modelDispatcher.KeywordPlural()),
- middleware(contactUpdateHandler), metadata, "contact_update", tags)
- // List
- app.AddHandler2("GET",
- fmt.Sprintf("%s/%s", prefix, modelDispatcher.KeywordPlural()),
- middleware(listHandler), metadata, "list_contacts", tags)
-
- app.AddHandler2("GET",
- fmt.Sprintf("%s/%s/users", prefix, modelDispatcher.KeywordPlural()),
- middleware(keyStoneUserListHandler), metadata, "list_users", tags)
-
- app.AddHandler2("GET",
- fmt.Sprintf("%s/%s/", prefix, modelDispatcher.KeywordPlural()),
- middleware(getHandler), metadata, "list_by_uid", tags)
-
- app.AddHandler2("POST",
- fmt.Sprintf("%s/%s/delete-contact", prefix, modelDispatcher.KeywordPlural()),
- middleware(deleteContactHandler), metadata, "delete", tags)
-
- // verify-trigger
- app.AddHandler2("POST",
- fmt.Sprintf("%s/%s//verify", prefix, modelDispatcher.KeywordPlural()),
- middleware(verifyTriggerHandler), metadata, "verify_trigger", tags)
-
- // Verify Handler, this modelDispatcher need db.DBModelDispatcher'Create function to create Contact so this modelDispatcher is
- // NotifyModelDispatcher whose DBModelDispatcher has modelManager models.ContactManager
- metadata, tags = map[string]interface{}{"manager": modelDispatcher}, map[string]string{"resource": models.VerifyManager.KeywordPlural()}
- app.AddHandler2("GET",
- fmt.Sprintf("%s/%s/", prefix, models.VerifyManager.KeywordPlural()),
- middleware(verifyHandler), metadata, "verify", tags)
-
- // notification Handler
- modelDispatcher = NewNotifyModelDispatcher(models.NotificationManager)
- metadata, tags = map[string]interface{}{"manager": modelDispatcher}, map[string]string{"resource": modelDispatcher.KeywordPlural()}
- app.AddHandler2("POST",
- fmt.Sprintf("%s/%s/", prefix, modelDispatcher.KeywordPlural()),
- middleware(notificationHandler), metadata, "send_notifications", tags)
- app.AddHandler2("GET",
- fmt.Sprintf("%s/%s/", prefix, modelDispatcher.KeywordPlural()),
- middleware(listHandler), metadata, "send_notifications", tags)
- app.AddHandler2("GET",
- fmt.Sprintf("%s/%s/", prefix, modelDispatcher.KeywordPlural()),
- middleware(listHandler), metadata, "list_notification_by_id", tags)
-
- // config Handler
- modelDispatcher = NewNotifyModelDispatcher(models.ConfigManager)
- metadata, tags = map[string]interface{}{"manager": modelDispatcher}, map[string]string{"resource": modelDispatcher.KeywordPlural()}
- app.AddHandler2("POST",
- fmt.Sprintf("%s/%s/", prefix, modelDispatcher.KeywordPlural()),
- middleware(configUpdateHandler), metadata, "update_configs", tags)
- app.AddHandler2("GET",
- fmt.Sprintf("%s/%s/", prefix, modelDispatcher.KeywordPlural()),
- middleware(configGetHandler), metadata, "get_configs", tags)
- app.AddHandler2("DELETE",
- fmt.Sprintf("%s/%s/", prefix, modelDispatcher.KeywordPlural()),
- middleware(configDeleteHandler), metadata, "delete_configs", tags)
- app.AddHandler2("POST",
- fmt.Sprintf("%s/%s//validate", prefix, modelDispatcher.KeywordPlural()),
- middleware(configValidateHandler), metadata, "validate_configs", tags)
-
- // email handler for being compatible
- app.AddHandler2("POST",
- fmt.Sprintf("%s/%s/", prefix, EMAIL_KEYWORDPLURAL),
- middleware(emailConfigUpdateHandler), metadata, "", tags)
- app.AddHandler2("GET",
- fmt.Sprintf("%s/%s/", prefix, EMAIL_KEYWORDPLURAL),
- middleware(emailConfigGetHandler), metadata, "", tags)
- app.AddHandler2("DELETE",
- fmt.Sprintf("%s/%s/", prefix, EMAIL_KEYWORDPLURAL),
- middleware(emailConfigDeleteHandler), metadata, "", tags)
- app.AddHandler2("PUT",
- fmt.Sprintf("%s/%s/", prefix, EMAIL_KEYWORDPLURAL),
- middleware(emailConfigUpdateHandler), metadata, "", tags)
-
- app.AddHandler2("POST",
- fmt.Sprintf("%s/%s/", prefix, SMS_KEYWORDPLURAL),
- middleware(smsConfigUpdateHandler), metadata, "", tags)
- app.AddHandler2("GET",
- fmt.Sprintf("%s/%s/", prefix, SMS_KEYWORDPLURAL),
- middleware(smsConfigGetHandler), metadata, "", tags)
- app.AddHandler2("DELETE",
- fmt.Sprintf("%s/%s/", prefix, SMS_KEYWORDPLURAL),
- middleware(smsConfigDeleteHandler), metadata, "", tags)
- app.AddHandler2("PUT",
- fmt.Sprintf("%s/%s/", prefix, SMS_KEYWORDPLURAL),
- middleware(smsConfigUpdateHandler), metadata, "", tags)
-
- // Template Handler
- modelDispatcher = NewNotifyModelDispatcher(models.TemplateManager)
- metadata, tags = map[string]interface{}{"manager": modelDispatcher}, map[string]string{"resource": modelDispatcher.KeywordPlural()}
- app.AddHandler2("POST",
- fmt.Sprintf("%s/%s//update-template", prefix, modelDispatcher.KeywordPlural()),
- middleware(templateUpdateHandler), metadata, "update_template", tags)
- // List
- app.AddHandler2("GET",
- fmt.Sprintf("%s/%s", prefix, modelDispatcher.KeywordPlural()),
- middleware(listHandler), metadata, "list_template", tags)
-
- app.AddHandler2("POST",
- fmt.Sprintf("%s/%s/delete-template", prefix, modelDispatcher.KeywordPlural()),
- middleware(deleteTemplateHandler), metadata, "delete", tags)
-}
-
-func templateUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, body := fetchEnv(ctx, w, r)
- data, err := body.GetArray(manager.Keyword(), manager.KeywordPlural())
- if err != nil {
- httperrors.GeneralServerError(ctx, w, httperrors.NewInputParameterError("need %s and %s",
- manager.Keyword(), manager.KeywordPlural()))
- return
- }
- ctype := params[""]
- if len(ctype) == 0 {
- httperrors.InputParameterError(ctx, w, "ctype of template should not be empty")
- }
- err = manager.UpdateTemplate(ctx, ctype, mergeQueryParams(params, query), data)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- }
-}
-
-func deleteTemplateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, _, query, body := fetchEnv(ctx, w, r)
- if !body.Contains("contact_type") {
- httperrors.InputParameterError(ctx, w, "miss contact_type")
- return
- }
- ctype, _ := body.GetString("contact_type")
- topic, _ := body.GetString("topic")
- err := manager.DeleteTemplate(ctx, query, ctype, topic)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- }
-}
-
-func configDeleteHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, _, _ := fetchEnv(ctx, w, r)
- err := manager.DeleteConfig(ctx, params)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- }
-}
-
-func configGetHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, _ := fetchEnv(ctx, w, r)
- ret, err := manager.GetConfig(ctx, params, query)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- appsrv.SendJSON(w, ret)
-}
-
-func configUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, _, _, body := fetchEnv(ctx, w, r)
- body, err := body.Get(models.ConfigManager.Keyword())
- if err != nil {
- httperrors.GeneralServerError(ctx, w, httperrors.NewInputParameterError("need config or configs"))
- return
- }
- err = manager.UpdateConfig(ctx, body)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- }
-}
-
-func configValidateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, _, body := fetchEnv(ctx, w, r)
- body, err := body.Get(models.ConfigManager.Keyword())
- if err != nil {
- httperrors.GeneralServerError(ctx, w, httperrors.NewInputParameterError("need config"))
- }
- ctype := params[""]
- err = manager.ValidateConfig(ctx, ctype, body)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
-}
-
-func notificationHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, _, _, body := fetchEnv(ctx, w, r)
- data, err := body.Get(manager.Keyword())
- if err != nil {
- httperrors.BadRequestError(ctx, w, "request body should contain %s", manager.Keyword())
- return
- }
- if !data.Contains("gid") && !data.Contains("uid") {
- httperrors.MissingParameterError(ctx, w, "gid | uid")
- return
- }
- _, err = manager.CreateNotification(ctx, data)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
-}
-
-// verify handler
-func verifyHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, _ := fetchEnv(ctx, w, r)
- err := manager.Verify(ctx, params, query)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- }
-}
-
-// contact update handler
-func contactUpdateHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, body := fetchEnv(ctx, w, r)
-
- var data []jsonutils.JSONObject
- data, err := body.GetArray(manager.Keyword(), manager.KeywordPlural())
- if err != nil {
- log.Errorf("body: %s, err: %s\n", body.String(), err)
- httperrors.GeneralServerError(ctx, w, httperrors.NewInputParameterError("need %s and %s",
- manager.Keyword(), manager.KeywordPlural()))
- return
- }
- log.Debugf("data: %s", data)
- pullCtypes, _ := body.GetArray(manager.Keyword(), "pull")
- log.Debugf("pullCtypes: %s", pullCtypes)
-
- uid := params[""]
- out, err := manager.UpdateContacts(ctx, uid, mergeQueryParams(params, query), data, pullCtypes, nil)
- if err != nil {
- log.Errorf(err.Error())
- httperrors.BadRequestError(ctx, w, "")
- return
- }
- if out == nil {
- return
- }
- appsrv.SendJSON(w, wrap(out, manager.Keyword()))
-}
-
-// delete contact handler
-func deleteContactHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, _, _, body := fetchEnv(ctx, w, r)
-
- var data []jsonutils.JSONObject
- var err error
- if body != nil {
- data, err = body.GetArray(manager.KeywordPlural())
- if err != nil {
- httperrors.BadRequestError(ctx, w, "request body should have %s", manager.KeywordPlural())
- return
- }
- }
- err = manager.DeleteContacts(ctx, data)
-
- if err != nil {
- log.Errorf("delete contact of %s failed, error: %s", data, err)
- httperrors.GeneralServerError(ctx, w, errors.Error("delete failed"))
- }
-}
-
-// verify trigger handler
-func verifyTriggerHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, _, body := fetchEnv(ctx, w, r)
- data, err := body.Get(models.ContactManager.Keyword())
- if err != nil {
- httperrors.BadRequestError(ctx, w, "request body should have %s", manager.Keyword())
- return
- }
- ret, err := manager.VerifyTrigger(ctx, params, data)
- if err != nil {
- log.Errorf("verifyTrigger failed beacause %s", err)
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- appsrv.SendJSON(w, ret)
-}
-
-// list handler for all resource in notify module
-func listHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, _ := fetchEnv(ctx, w, r)
- listResult, err := manager.List(ctx, mergeQueryParams(params, query), nil)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- appsrv.SendJSON(w, modulebase.ListResult2JSONWithKey(listResult, manager.KeywordPlural()))
-}
-
-func getHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, params, query, _ := fetchEnv(ctx, w, r)
- listResult, err := manager.List(ctx, mergeQueryParams(params, query), nil)
- if err != nil {
- httperrors.GeneralServerError(ctx, w, err)
- return
- }
- var data jsonutils.JSONObject
- if len(listResult.Data) == 0 {
- data = jsonutils.NewDict()
- } else {
- data = listResult.Data[0]
- }
- appsrv.SendJSON(w, wrap(data, manager.Keyword()))
-}
-
-func wrap(data jsonutils.JSONObject, key string) jsonutils.JSONObject {
- ret := jsonutils.NewDict()
- ret.Add(data, key)
- return ret
-}
-
-// offset ang limit is not useable for here
-func keyStoneUserListHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
- manager, _, query, _ := fetchEnv(ctx, w, r)
- haveContacts := jsonutils.QueryBoolean(query, "have_contacts", false)
-
- userCred := policy.FetchUserCredential(ctx)
- s := auth.GetSession(ctx, userCred, options.Options.Region, "")
- users, err := modules.UsersV3.List(s, query)
- if err != nil {
- log.Errorf("keystone list error: %s", err)
- httperrors.InternalServerError(ctx, w, err.Error())
- return
- }
- q := models.ContactManager.Query("uid").GroupBy("uid")
- row, err := q.Rows()
- if err != nil {
- log.Errorf("get contact's uid error: %s", err)
- httperrors.InternalServerError(ctx, w, err.Error())
- return
- }
- defer row.Close()
- uidSet, uid := make(map[string]struct{}), ""
- for row.Next() {
- row.Scan(&uid)
- uidSet[uid] = struct{}{}
- }
-
- type sPair struct {
- ID string
- Name string
- }
- newDatas := make([]sPair, 0, len(users.Data))
-
- for _, data := range users.Data {
- id, _ := data.GetString("id")
- name, _ := data.GetString("name")
- if _, ok := uidSet[id]; ok {
- if haveContacts {
- newDatas = append(newDatas, sPair{id, name})
- }
- continue
- }
- if haveContacts {
- continue
- }
- newDatas = append(newDatas, sPair{id, name})
- }
- ret := jsonutils.NewDict()
- ret.Add(jsonutils.Marshal(newDatas), manager.Keyword())
- appsrv.SendJSON(w, ret)
-}
diff --git a/pkg/notify/interface/interface.go b/pkg/notify/interface.go
similarity index 91%
rename from pkg/notify/interface/interface.go
rename to pkg/notify/interface.go
index 21d72105e2..5f24369a28 100644
--- a/pkg/notify/interface/interface.go
+++ b/pkg/notify/interface.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package _interface
+package notify
import (
"context"
@@ -28,6 +28,7 @@ type INotifyService interface {
RestartService(ctx context.Context, config SConfig, serviceName string)
Send(ctx context.Context, contactType, contact, topic, msg, priority string) error
ContactByMobile(ctx context.Context, mobile, serviceName string) (string, error)
+ BatchSend(ctx context.Context, contacts []string, contactType, topic, message, priority string) ([]*apis.FailedRecord, error)
ValidateConfig(ctx context.Context, cType string, configs map[string]string) (isValid bool, message string, err error)
}
diff --git a/pkg/notify/models/config.go b/pkg/notify/models/config.go
new file mode 100644
index 0000000000..c7eec3d823
--- /dev/null
+++ b/pkg/notify/models/config.go
@@ -0,0 +1,296 @@
+// Copyright 2019 Yunion
+//
+// 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 models
+
+import (
+ "context"
+ "database/sql"
+
+ "yunion.io/x/jsonutils"
+ "yunion.io/x/pkg/errors"
+ "yunion.io/x/pkg/utils"
+ "yunion.io/x/sqlchemy"
+
+ api "yunion.io/x/onecloud/pkg/apis/notify"
+ "yunion.io/x/onecloud/pkg/cloudcommon/db"
+ "yunion.io/x/onecloud/pkg/httperrors"
+ "yunion.io/x/onecloud/pkg/mcclient"
+ "yunion.io/x/onecloud/pkg/mcclient/auth"
+ notifyv2 "yunion.io/x/onecloud/pkg/notify"
+ "yunion.io/x/onecloud/pkg/notify/oldmodels"
+ "yunion.io/x/onecloud/pkg/notify/options"
+ "yunion.io/x/onecloud/pkg/util/stringutils2"
+)
+
+type SConfigManager struct {
+ db.SStandaloneResourceBaseManager
+}
+
+var ConfigManager *SConfigManager
+
+func init() {
+ ConfigManager = &SConfigManager{
+ SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
+ SConfig{},
+ "configs_tbl",
+ "notifyconfig",
+ "notifyconfigs",
+ ),
+ }
+ ConfigManager.SetVirtualObject(ConfigManager)
+}
+
+type SConfig struct {
+ db.SStandaloneResourceBase
+
+ Type string `width:"15" nullable:"false" create:"required" get:"admin" list:"admin"`
+ Content jsonutils.JSONObject `nullable:"false" create:"required" update:"admin" get:"admin" list:"admin"`
+}
+
+func (cm *SConfigManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.ConfigCreateInput) (api.ConfigCreateInput, error) {
+ var err error
+ input.StandaloneResourceCreateInput, err = cm.SStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StandaloneResourceCreateInput)
+ if err != nil {
+ return input, err
+ }
+ if !utils.IsInStringArray(input.Type, []string{api.EMAIL, api.MOBILE, api.DINGTALK, api.FEISHU, api.WEBCONSOLE, api.WORKWX, api.FEISHU_ROBOT, api.DINGTALK_ROBOT, api.WORKWX_ROBOT}) {
+ return input, httperrors.NewInputParameterError("unkown type %q", input.Type)
+ }
+ if input.Content == nil {
+ return input, httperrors.NewMissingParameterError("content")
+ }
+ config, err := cm.GetConfigByType(input.Type)
+ if err == nil && config != nil {
+ return input, httperrors.NewDuplicateResourceError("duplicate type %q", input.Type)
+ }
+ if err != nil && errors.Cause(err) != sql.ErrNoRows {
+ return input, err
+ }
+ // validate
+ configs := make(map[string]string)
+ err = input.Content.Unmarshal(&configs)
+ if err != nil {
+ return input, err
+ }
+ isValid, message, err := NotifyService.ValidateConfig(ctx, input.Type, configs)
+ if err != nil {
+ if errors.Cause(err) == errors.ErrNotImplemented {
+ return input, httperrors.NewNotImplementedError("validating config of %s", input.Type)
+ }
+ return input, err
+ }
+ if !isValid {
+ return input, httperrors.NewInputParameterError(message)
+ }
+ if len(input.Name) == 0 {
+ input.Name = input.Type
+ }
+ return input, nil
+}
+
+func (self *SConfigManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.ConfigListInput) (*sqlchemy.SQuery, error) {
+ q, err := self.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StandaloneResourceListInput)
+ if err != nil {
+ return nil, err
+ }
+ q = q.NotEquals("type", api.WEBCONSOLE)
+ if len(input.Type) > 0 {
+ q.Filter(sqlchemy.Equals(q.Field("type"), input.Type))
+ }
+ return q, nil
+}
+
+func (cm *SConfigManager) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, isList bool) (api.ConfigDetails, error) {
+ return api.ConfigDetails{}, nil
+}
+
+func (cm *SConfigManager) FetchCustomizeColumns(
+ ctx context.Context,
+ userCred mcclient.TokenCredential,
+ query jsonutils.JSONObject,
+ objs []interface{},
+ fields stringutils2.SSortedStrings,
+ isList bool,
+) []api.ConfigDetails {
+ sRows := cm.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
+ rows := make([]api.ConfigDetails, len(objs))
+ for i := range rows {
+ rows[i].StandaloneResourceDetails = sRows[i]
+ }
+ return rows
+}
+
+func (cm *SConfigManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
+ q, err := cm.SStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
+ if err != nil {
+ return q, nil
+ }
+ return q, nil
+}
+
+func (cm *SConfigManager) OrderByExtraFields(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query api.ConfigListInput) (*sqlchemy.SQuery, error) {
+ q, err := cm.SStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StandaloneResourceListInput)
+ if err != nil {
+ return nil, err
+ }
+ return q, nil
+}
+
+func (cm *SConfigManager) AllowPerformValidate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
+ return db.IsAdminAllowPerform(userCred, cm, "validate")
+}
+
+func (cm *SConfigManager) PerformValidate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ConfigValidateInput) (api.ConfigValidateOutput, error) {
+ var (
+ output api.ConfigValidateOutput
+ err error
+ )
+ if !utils.IsInStringArray(input.Type, []string{api.EMAIL, api.MOBILE, api.DINGTALK, api.FEISHU, api.WEBCONSOLE, api.WORKWX, api.FEISHU_ROBOT, api.DINGTALK_ROBOT, api.WORKWX_ROBOT}) {
+ return output, httperrors.NewInputParameterError("unkown type %q", input.Type)
+ }
+ if input.Content == nil {
+ return output, httperrors.NewMissingParameterError("content")
+ }
+ // validate
+ configs := make(map[string]string)
+ err = input.Content.Unmarshal(&configs)
+ if err != nil {
+ return output, err
+ }
+ isValid, message, err := NotifyService.ValidateConfig(ctx, input.Type, configs)
+ if err != nil {
+ if errors.Cause(err) == errors.ErrNotImplemented {
+ return output, httperrors.NewNotImplementedError("validating config of %s", input.Type)
+ }
+ return output, err
+ }
+ if !isValid {
+ output.IsValid = false
+ output.Message = message
+ } else {
+ output.IsValid = true
+ }
+ return output, nil
+}
+
+func (self *SConfigManager) InitializeData() error {
+ ctx := context.Background()
+ userCred := auth.AdminCredential()
+ // fetch all configs
+ configs := make([]oldmodels.SConfig, 0, 5)
+ q := oldmodels.ConfigManager.Query()
+ err := db.FetchModelObjects(oldmodels.ConfigManager, q, &configs)
+ if err != nil {
+ return errors.Wrap(err, "db.FetchModelObjects")
+ }
+
+ // build type==>config map
+ tcMap := make(map[string][]*oldmodels.SConfig)
+ for i := range configs {
+ t := configs[i].Type
+ if _, ok := tcMap[t]; !ok {
+ tcMap[t] = make([]*oldmodels.SConfig, 0, 3)
+ }
+ tcMap[t] = append(tcMap[t], &configs[i])
+ }
+
+ for t, configs := range tcMap {
+ cMap := make(map[string]string)
+ for _, config := range configs {
+ cMap[config.KeyText] = config.ValueText
+ }
+ newConfig := SConfig{
+ Type: t,
+ Content: jsonutils.Marshal(cMap),
+ }
+ err := self.TableSpec().Insert(ctx, &newConfig)
+ if err != nil {
+ return errors.Wrap(err, "TableSpec().Insert")
+ }
+ for _, config := range configs {
+ err := config.Delete(ctx, userCred)
+ if err != nil {
+ return errors.Wrap(err, "Delete")
+ }
+ }
+ }
+
+ // init webconsole's config
+ q = self.Query().Equals("type", api.WEBCONSOLE)
+ wsConfigs := make([]SConfig, 0, 2)
+ err = db.FetchModelObjects(self, q, &wsConfigs)
+ if err != nil && errors.Cause(err) != sql.ErrNoRows {
+ return errors.Wrap(err, "db.FetchModelObjects")
+ }
+ if len(wsConfigs) > 1 {
+ for i := 1; i < len(wsConfigs); i++ {
+ err := wsConfigs[i].Delete(ctx, userCred)
+ if err != nil {
+ return errors.Wrap(err, "Delete redundant")
+ }
+ }
+ }
+
+ var config *SConfig
+ if len(wsConfigs) > 0 {
+ config = &wsConfigs[0]
+ } else {
+ config = &SConfig{
+ Type: api.WEBCONSOLE,
+ }
+ }
+ config.Content = jsonutils.Marshal(map[string]string{
+ "auth_uri": options.Options.AuthURL,
+ "admin_user": options.Options.AdminUser,
+ "admin_password": options.Options.AdminPassword,
+ "admin_tenant_name": options.Options.AdminProject,
+ })
+ err = self.TableSpec().InsertOrUpdate(context.TODO(), config)
+ return nil
+}
+
+// Fetch all SConfig struct which type is contactType.
+func (self *SConfigManager) GetConfigByType(contactType string) (*SConfig, error) {
+ var config SConfig
+ q := self.Query()
+ q.Filter(sqlchemy.Equals(q.Field("type"), contactType))
+ err := q.First(&config)
+ if err != nil {
+ return nil, errors.Wrap(err, "fail to fetch SConfigs by type")
+ }
+ return &config, nil
+}
+
+func (self *SConfigManager) GetConfig(contactType string) (notifyv2.SConfig, error) {
+ config, err := self.GetConfigByType(contactType)
+ if err != nil {
+ return nil, err
+ }
+ ret := make(map[string]string)
+ err = config.Content.Unmarshal(&ret)
+ if err != nil {
+ return nil, errors.Wrap(err, "fail unmarshal config content")
+ }
+ return ret, nil
+}
+
+func (self *SConfigManager) SetConfig(contactType string, config notifyv2.SConfig) error {
+ content := jsonutils.Marshal(config)
+ sConfig := &SConfig{
+ Type: contactType,
+ Content: content,
+ }
+ return self.TableSpec().InsertOrUpdate(context.Background(), sConfig)
+}
diff --git a/pkg/notify/models/consts.go b/pkg/notify/models/consts.go
deleted file mode 100644
index 39cbe63ab6..0000000000
--- a/pkg/notify/models/consts.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 models
-
-const (
- EMAIL = "email"
- MOBILE = "mobile"
- DINGTALK = "dingtalk"
- FEISHU = "feishu"
- WEBCONSOLE = "webconsole"
- ROBOT = "robot"
-
- NOTIFY_RECEIVED = "received" // Received a task about sending a notification
- NOTIFY_SENT = "sent" // Nofity module has sent notification, but result unkown
- NOTIFY_OK = "sent_ok" // Notification was sent successfully
- NOTIFY_FAIL = "sent_fail" // That sent a notification is failed
-
- CONTACT_INIT = "init" // Contact's status is init which means no verifying
- CONTACT_VERIFYING = "verifying" // Contact's status is verifying
- CONTACT_VERIFIED = "verified" // Contact's status is verified
-
- VERIFICATION_SENT = "sent" // Verification was sent
- VERIFICATION_SENT_FAIL = "sent_fail" // Verification was sent failed
- VERIFICATION_VERIFIED = "verified" // Verification was verified
- VERIFICATION_TOKEN_EXPIRED = "Verification code expired"
- VERIFICATION_TOKEN_INVALID = "Incorrect verification code"
-)
-
-// Dingtalk account will be update automatically as mobile number change so that update dingtalk is not allowed
-// In webconsole, uid is the same as contact.
-var UpdateNotAllow = map[string]struct{}{
- DINGTALK: {},
- FEISHU: {},
- WEBCONSOLE: {},
-}
diff --git a/pkg/notify/models/initdb.go b/pkg/notify/models/initdb.go
index f82e4a4760..0a9d0901ac 100644
--- a/pkg/notify/models/initdb.go
+++ b/pkg/notify/models/initdb.go
@@ -27,8 +27,7 @@ func InitDB() error {
* initialization order matters, do not change the order
*/
- ContactManager,
- VerifyManager,
+ ReceiverManager,
NotificationManager,
ConfigManager,
TemplateManager,
diff --git a/pkg/notify/models/mod_config.go b/pkg/notify/models/mod_config.go
deleted file mode 100644
index 521284ff84..0000000000
--- a/pkg/notify/models/mod_config.go
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 models
-
-import (
- "context"
- "fmt"
- "strconv"
-
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/sqlchemy"
-
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/mcclient"
- _interface "yunion.io/x/onecloud/pkg/notify/interface"
- "yunion.io/x/onecloud/pkg/notify/options"
-)
-
-type SConfigManager struct {
- SStatusStandaloneResourceBaseManager
-}
-
-var ConfigManager *SConfigManager
-
-func init() {
- ConfigManager = &SConfigManager{
- SStatusStandaloneResourceBaseManager: NewStatusStandaloneResourceBaseManager(
- SConfig{},
- "notify_t_config",
- "config",
- "configs",
- ),
- }
- ConfigManager.SetVirtualObject(ConfigManager)
-}
-
-// SConfig is a table which storage (k,v) and its type.
-// The three important concepts are key, value and type.
-// Key and type uniquely identify a value.
-type SConfig struct {
- SStatusStandaloneResourceBase
-
- Type string `width:"15" nullable:"false" create:"required" list:"user"`
- KeyText string `width:"30" nullable:"false" create:"required" list:"user"`
- ValueText string `width:"256" nullable:"false" create:"required" list:"user"`
-}
-
-// ListItemFilter is a hook function belong to IModelManager interface when Listing.
-// This will Called in yunion.io/x/onecloud/pkg/cloudcommon/db.List function.
-func (self *SConfigManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
- if !query.Contains("type") {
- return q, nil
- }
- contactType, _ := query.GetString("type")
- q.Filter(sqlchemy.Equals(q.Field("type"), contactType))
- return q, nil
-}
-
-// GetValue fetch the SConfig struct corresponding to key and type.
-func (self *SConfigManager) GetValue(key, contactType string) (*SConfig, error) {
- q := self.Query()
- q.Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("type"), contactType), sqlchemy.Equals(q.Field("key_text"), key)))
- configs := make([]SConfig, 0, 1)
- err := db.FetchModelObjects(self, q, &configs)
- if err != nil {
- return nil, errors.Wrap(err, "Fetch SConfig by key and type failed")
- }
- if len(configs) == 0 {
- return nil, errors.Error("There is no SConfig whose key and type meet the requirement")
- }
- return &configs[0], nil
-}
-
-func (self *SConfigManager) InitializeData() error {
- q := self.Query()
- q = q.Filter(sqlchemy.OR(sqlchemy.IsNotNull(q.Field("updated_at")), sqlchemy.IsNotNull(q.Field("created_at")),
- sqlchemy.IsNotNull(q.Field("deleted_at")), sqlchemy.IsTrue(q.Field("deleted")),
- sqlchemy.IsNotNull(q.Field("update_by")), sqlchemy.IsNotNull(q.Field("delete_by"))))
- n, err := q.CountWithError()
- if err != nil {
- return err
- }
- if n <= 0 {
- log.Debugf("need to init data for %s", self.TableSpec().Name())
- // need to update
- sql := fmt.Sprintf("update %s set updated_at=gmt_modified, deleted=is_deleted, created_at=gmt_create, deleted_at=gmt_deleted, update_by=modified_by, delete_by=deleted_by", self.TableSpec().Name())
- q := sqlchemy.NewRawQuery(sql, "")
- q.Row()
- sql = fmt.Sprintf("update %s set type='mobile' where type='sms_aliyun'", self.TableSpec().Name())
- q = sqlchemy.NewRawQuery(sql, "")
- q.Row()
- }
-
- // init webconsole's config
- sql := fmt.Sprintf("update %s set deleted='1' where type='webconsole'", self.TableSpec().Name())
- q = sqlchemy.NewRawQuery(sql)
- q.Row()
- configs := []SConfig{
- {
- Type: "webconsole",
- KeyText: "auth_uri",
- ValueText: options.Options.AuthURL,
- },
- {
- Type: "webconsole",
- KeyText: "admin_user",
- ValueText: options.Options.AdminUser,
- },
- {
- Type: "webconsole",
- KeyText: "admin_password",
- ValueText: options.Options.AdminPassword,
- },
- {
- Type: "webconsole",
- KeyText: "admin_tenant_name",
- ValueText: options.Options.AdminProject,
- },
- }
- for _, config := range configs {
- err := self.TableSpec().Insert(context.TODO(), &config)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-// Fetch all SConfig struct which type is contactType.
-func (self *SConfigManager) GetConfigByType(contactType string) ([]SConfig, error) {
- q := self.Query()
- q.Filter(sqlchemy.Equals(q.Field("type"), contactType))
- configs := make([]SConfig, 0, 5)
- err := db.FetchModelObjects(self, q, &configs)
- if err != nil {
- return nil, errors.Wrap(err, "Fetch SConfigs by type failed")
- }
- //if len(configs) == 0 {
- // return nil, errors.Error("There is no SConfig whose type meet the requirement")
- //}
- return configs, nil
-}
-
-func (self *SConfigManager) GetConfig(contactType string) (_interface.SConfig, error) {
- configs, err := self.GetConfigByType(contactType)
- if err != nil {
- return nil, err
- }
- ret := make(map[string]string)
- for i := range configs {
- ret[configs[i].KeyText] = configs[i].ValueText
- }
- return ret, nil
-}
-
-func (self *SConfigManager) SetConfig(contactType string, config _interface.SConfig) error {
- return fmt.Errorf("SetConfig Not Implemented")
-}
-
-type SConvertFunc func(*jsonutils.JSONDict) *jsonutils.JSONDict
-
-var (
- // toDisplay store SConvertFunc which convert the data from client to the form required by database of contactType
- toDisplay map[string]SConvertFunc
- // fromDisplay store SConvertFunc which convert the data from database to the form required by client of contactType
- fromDisplay map[string]SConvertFunc
-)
-
-func init() {
- toDisplay = map[string]SConvertFunc{
- EMAIL: emailToDisplay,
- }
- fromDisplay = map[string]SConvertFunc{
- EMAIL: emailFromDisplay,
- }
-}
-
-func emailFromDisplay(dict *jsonutils.JSONDict) *jsonutils.JSONDict {
- keys := dict.SortedKeys()
- newKey := ""
- for _, key := range keys {
- switch key {
- case "username", "password":
- newKey = "mail." + key
- case "hostname", "hostport":
- newKey = "mail.smtp." + key
- case "ssl_global":
- newKey = "mail.global.ssl"
- }
- v, _ := dict.Get(key)
- dict.Add(v, newKey)
- dict.Remove(key)
- }
- return dict
-}
-
-func emailToDisplay(dict *jsonutils.JSONDict) *jsonutils.JSONDict {
- keys := dict.SortedKeys()
- for _, key := range keys {
- newKey := ""
- value, _ := dict.Get(key)
- switch key {
- case "mail.username", "mail.password":
- newKey = key[5:]
- case "mail.smtp.hostname":
- newKey = key[10:]
- case "mail.smtp.hostport":
- newKey = key[10:]
- portStr, _ := value.GetString()
- port, _ := strconv.Atoi(portStr)
- value = jsonutils.NewInt(int64(port))
- case "mail.global.ssl":
- newKey = "ssl_global"
- sslStr, _ := value.GetString()
- ssl, _ := strconv.ParseBool(sslStr)
- value = jsonutils.NewBool(ssl)
- }
- dict.Add(value, newKey)
- dict.Remove(key)
- }
- return dict
-}
-
-func (self *SConfigManager) Display2Database(cType string, dict *jsonutils.JSONDict) *jsonutils.JSONDict {
- cf, ok := fromDisplay[cType]
- if ok {
- return cf(dict)
- }
- return dict
-}
-
-func (self *SConfigManager) Database2Display(cType string, dict *jsonutils.JSONDict) *jsonutils.JSONDict {
- cf, ok := toDisplay[cType]
- if ok {
- return cf(dict)
- }
- return dict
-}
diff --git a/pkg/notify/models/mod_contact.go b/pkg/notify/models/mod_contact.go
deleted file mode 100644
index 2eeee505dd..0000000000
--- a/pkg/notify/models/mod_contact.go
+++ /dev/null
@@ -1,404 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 models
-
-import (
- "context"
- "database/sql"
- "fmt"
- "strings"
- "time"
-
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/pkg/util/sets"
- "yunion.io/x/sqlchemy"
-
- api "yunion.io/x/onecloud/pkg/apis/notify"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/auth"
- "yunion.io/x/onecloud/pkg/mcclient/modules"
- "yunion.io/x/onecloud/pkg/notify/utils"
- "yunion.io/x/onecloud/pkg/util/rbacutils"
- "yunion.io/x/onecloud/pkg/util/stringutils2"
-)
-
-type SContactManager struct {
- SStatusStandaloneResourceBaseManager
-}
-
-var ContactManager *SContactManager
-
-func init() {
- ContactManager = &SContactManager{
- SStatusStandaloneResourceBaseManager: NewStatusStandaloneResourceBaseManager(
- SContact{},
- "notify_t_contacts",
- "contact",
- "contacts",
- ),
- }
- ContactManager.SetVirtualObject(ContactManager)
-}
-
-type SContact struct {
- SStatusStandaloneResourceBase
-
- UID string `width:"128" nullable:"false" create:"required" update:"user" list:"user" get:"user"`
- ContactType string `width:"16" nullable:"false" create:"required" update:"user"`
- Contact string `width:"64" nullable:"false" create:"required" update:"user"`
- Enabled string `width:"5" nullable:"false" default:"1" create:"optional" update:"user"`
- VerifiedAt time.Time `update:"user"`
-}
-
-func (self *SContactManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
- return true
-}
-
-func (self *SContactManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
- return true
-}
-
-func (self *SContactManager) ResourceScope() rbacutils.TRbacScope {
- return rbacutils.ScopeUser
-}
-
-func (self *SContactManager) NamespaceScope() rbacutils.TRbacScope {
- return rbacutils.ScopeUser
-}
-
-func (self *SContactManager) FetchOwnerId(ctx context.Context,
- data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) {
-
- return db.FetchUserInfo(ctx, data)
-}
-
-func (self *SContactManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider,
- scope rbacutils.TRbacScope) *sqlchemy.SQuery {
- if owner != nil {
- if scope == rbacutils.ScopeUser {
- if len(owner.GetUserId()) > 0 {
- q = q.Equals("uid", owner.GetUserId())
- }
- }
- }
- return q
-}
-
-func (self *SContactManager) InitializeData() error {
- q := self.Query()
- q = q.Filter(sqlchemy.OR(sqlchemy.IsNotNull(q.Field("updated_at")), sqlchemy.IsTrue(q.Field("deleted"))))
- n, err := q.CountWithError()
- if err != nil {
- return err
- }
- if n > 0 {
- log.Debugf("no need to init data for %s", self.TableSpec().Name())
- // no need to init data
- return nil
- }
- log.Debugf("need to init data for %s", self.TableSpec().Name())
- sql := fmt.Sprintf("update %s set updated_at=update_at, deleted=is_deleted", self.TableSpec().Name())
- q = sqlchemy.NewRawQuery(sql, "")
- q.Row()
- 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 {
- uids, err = self._UIDsFromUIDOrName(ctx, uids)
- if err != nil {
- return nil, err
- }
- }
- q := self.Query()
- q = q.Filter(sqlchemy.In(q.Field("uid"), uids))
- records := make([]SContact, 0, len(uids))
- err = db.FetchModelObjects(self, q, &records)
- if err != nil {
- return nil, err
- }
- return records, nil
-}
-
-func (self *SContactManager) _UIDsFromUIDOrName(ctx context.Context, uidStrs []string) ([]string, error) {
- users, err := utils.GetUsersWithoutRemote(ctx, uidStrs)
- if err != nil {
- return nil, err
- }
- uids := make([]string, 0, len(uidStrs))
- uidSet := sets.NewString(uidStrs...)
- var (
- uid string
- uname string
- )
- for i := range users {
- uid = users[i].Id
- uname = users[i].Name
- if uidSet.Has(uid) {
- uids = append(uids, uid)
- uidSet.Delete(uid)
- continue
- }
- if uidSet.Has(uname) {
- uids = append(uids, uid)
- uidSet.Delete(uname)
- continue
- }
- }
- for _, uid = range uidSet.UnsortedList() {
- uids = append(uids, uid)
- }
- log.Debugf("uids %s => %s", uidStrs, uids)
- return uids, nil
-}
-
-func (self *SContactManager) FetchByUIDAndCType(uid string, contactTypes []string) ([]SContact, error) {
- q := self.Query("id", "uid", "contact_type", "contact", "enabled").Equals("uid", uid).In("contact_type", contactTypes)
- records := make([]SContact, 0, len(contactTypes))
- err := db.FetchModelObjects(self, q, &records)
- if err != nil {
- return nil, err
- }
- return records, nil
-}
-
-func (self *SContactManager) FetchByMore(uid, contact, contactType string) ([]SContact, error) {
- q := self.Query().Equals("uid", uid).Equals("contact", contact).Equals("contact_type", contactType)
- records := make([]SContact, 0, 1)
- err := db.FetchModelObjects(self, q, &records)
- if err != nil {
- return nil, err
- }
- return records, nil
-}
-
-func (self *SContact) getMoreDetail(ctx context.Context, userCred mcclient.TokenCredential,
- out api.ContactDetails) (api.ContactDetails, error) {
-
- uname, err := utils.GetUsernameByID(ctx, self.UID)
- if errors.Cause(err) == sql.ErrNoRows {
- uname = self.UID
- err = nil
- }
- if err != nil {
- return out, err
- }
-
- q := ContactManager.Query().Equals("uid", self.UID)
- contacts := make([]SContact, 0)
- err = db.FetchModelObjects(ContactManager, q, &contacts)
- if err != nil {
- return out, errors.Wrapf(err, "fetch Contacts of uid %s error", self.UID)
- }
- out.UID = self.UID
- out.Name = uname
- out.Details = jsonutils.Marshal(contacts).String()
-
- return out, nil
-}
-
-func (manager *SContactManager) FetchCustomizeColumns(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- objs []interface{},
- fields stringutils2.SSortedStrings,
- isList bool,
-) []api.ContactDetails {
- rows := make([]api.ContactDetails, len(objs))
-
- stdRows := manager.SStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
-
- var err error
- for i := range rows {
- rows[i] = api.ContactDetails{
- ResourceBaseDetails: stdRows[i],
- }
- rows[i], err = objs[i].(*SContact).getMoreDetail(ctx, userCred, rows[i])
- if err != nil {
- log.Errorf(err.Error())
- }
- }
- return rows
-}
-
-func (self *SContact) GetExtraDetails(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- isList bool,
-) (api.ContactDetails, error) {
- return api.ContactDetails{}, nil
-}
-
-// 联系方式列表
-func (self *SContactManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
- queryDict := query.(*jsonutils.JSONDict)
- if queryDict.Contains("uid") {
- uid, _ := queryDict.GetString("uid")
- q = q.Equals("uid", uid)
- }
- // for now
- if queryDict.Contains("filter") {
- filterCon, _ := queryDict.GetString("filter")
- queryDict.Remove("filter")
- contain := "name.contains("
- index := strings.Index(filterCon, contain)
- if index < 0 {
- return q, nil
- }
- filterCon = filterCon[index+len(contain):]
- index = strings.Index(filterCon, ")")
- if index < 0 {
- return q, nil
- }
- name := filterCon[:index]
- ids, err := utils.GetUserIdsLikeName(ctx, name)
- if err != nil {
- return q, nil
- }
- q = q.In("uid", ids)
- }
-
- scopeStr, err := query.GetString("scope")
- if err != nil {
- scopeStr = "project"
- }
- scope := rbacutils.TRbacScope(scopeStr)
-
- switch {
- case scope.HigherEqual(rbacutils.ScopeSystem):
- case scope.HigherEqual(rbacutils.ScopeDomain):
- uids, err := self.uidsInDomain(ctx, userCred)
- if err != nil {
- return q, err
- }
- q = q.In("uid", uids)
- default:
- q = q.Equals("uid", userCred.GetUserId())
- }
-
- q = q.GroupBy("uid").Desc("created_at")
-
- return q, nil
-}
-
-func (self *SContactManager) uidsInDomain(ctx context.Context, userCred mcclient.TokenCredential) ([]string, error) {
- session := auth.GetSession(ctx, userCred, "", "")
- params := jsonutils.NewDict()
- params.Set("scope", jsonutils.NewString("domain"))
- ret, err := modules.UsersV3.List(session, params)
- if err != nil {
- return nil, errors.Wrap(err, "modules.Userv3.List")
- }
- uids := make([]string, 0, len(ret.Data))
- for i := range ret.Data {
- id, _ := ret.Data[i].GetString("id")
- uids = append(uids, id)
- }
- return uids, nil
-}
-
-// Contacts query all contacts by uids and contactType
-func (self *SContactManager) Contacts(uids []string, contactType string) ([]SContact, error) {
- contacts := make([]SContact, 0, len(uids))
- if contactType == WEBCONSOLE {
- for _, uid := range uids {
- contacts = append(contacts, SContact{
- UID: uid,
- ContactType: WEBCONSOLE,
- Contact: uid,
- })
- }
- return contacts, nil
- }
-
- queryCon := contactType
- if strings.Contains(contactType, ROBOT) {
- queryCon = MOBILE
- }
- q := self.Query().Equals("contact_type", queryCon).Equals("enabled", "1").In("uid", uids)
- err := db.FetchModelObjects(self, q, &contacts)
- if err != nil {
- return nil, err
- }
-
- // For Robot Sender, only one message of the same content is sent for multiple users,
- // so the user's contact information is a collection of all contact information
- if strings.Contains(contactType, ROBOT) {
- // hack
- contactVals := make([]string, len(contacts))
- uidVals := make([]string, len(contacts))
- for i := range contacts {
- contactVals[i] = contacts[i].Contact
- uidVals[i] = contacts[i].UID
- }
- contacts = []SContact{
- {
- UID: strings.Join(uidVals, ","),
- ContactType: contactType,
- Contact: strings.Join(contactVals, ","),
- },
- }
- }
- return contacts, nil
-}
-
-func (self *SContactManager) GetAllNotify(ctx context.Context, ids []string, contactType string, group bool) ([]SContact, error) {
- var uids []string
- var err error
-
- if !group {
- if v := ctx.Value("uname"); v != nil {
- ids, err = self._UIDsFromUIDOrName(ctx, ids)
- if err != nil {
- return nil, errors.Wrap(err, "fail to transfer array of UID or Uname to UIDs")
- }
- }
- uids = ids
- } else {
- uid := make([]string, 0)
- for _, id := range uids {
- tmpUids, err := utils.GetUsersByGroupID(ctx, id)
- if err != nil {
- return nil, err
- }
- uid = append(uid, tmpUids...)
- }
- uids = uid
- }
- return self.Contacts(uids, contactType)
-}
-
-type SContactResponse struct {
- Id string
- Name string
- Details string
-}
-
-func NewSContactResponse(ctx context.Context, uid string, details string) SContactResponse {
- name, _ := utils.GetUsernameByID(ctx, uid)
- return SContactResponse{
- Id: uid,
- Name: name,
- Details: details,
- }
-}
diff --git a/pkg/notify/models/mod_notification.go b/pkg/notify/models/mod_notification.go
deleted file mode 100644
index 4e6fcbc857..0000000000
--- a/pkg/notify/models/mod_notification.go
+++ /dev/null
@@ -1,506 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 models
-
-import (
- "bytes"
- "context"
- "fmt"
- "strings"
- "time"
-
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- "yunion.io/x/sqlchemy"
-
- api "yunion.io/x/onecloud/pkg/apis/notify"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/cloudcommon/policy"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/notify/cache"
- _interface "yunion.io/x/onecloud/pkg/notify/interface"
- "yunion.io/x/onecloud/pkg/notify/options"
- "yunion.io/x/onecloud/pkg/notify/utils"
- "yunion.io/x/onecloud/pkg/util/rbacutils"
- "yunion.io/x/onecloud/pkg/util/stringutils2"
-)
-
-type SNotificationManager struct {
- SStatusStandaloneResourceBaseManager
-}
-
-var NotificationManager *SNotificationManager
-var NotifyService _interface.INotifyService
-
-func init() {
- NotificationManager = &SNotificationManager{
- SStatusStandaloneResourceBaseManager: NewStatusStandaloneResourceBaseManager(
- SNotification{},
- "notify_t_notification",
- "notification",
- "notifications",
- ),
- }
- NotificationManager.SetVirtualObject(NotificationManager)
-}
-
-func (self *SNotificationManager) ResourceScope() rbacutils.TRbacScope {
- return rbacutils.ScopeUser
-}
-
-func (self *SNotificationManager) NamespaceScope() rbacutils.TRbacScope {
- return rbacutils.ScopeUser
-}
-
-func (self *SNotificationManager) FetchOwnerId(ctx context.Context,
- data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) {
-
- return db.FetchUserInfo(ctx, data)
-}
-
-func (self *SNotificationManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider,
- scope rbacutils.TRbacScope) *sqlchemy.SQuery {
- if owner != nil {
- if scope == rbacutils.ScopeUser {
- if len(owner.GetUserId()) > 0 {
- q = q.Equals("uid", owner.GetUserId())
- }
- }
- }
- return q
-}
-
-type SNotification struct {
- SStatusStandaloneResourceBase
-
- UID string `width:"128" nullable:"false" create:"required"`
- ContactType string `width:"16" nullable:"false" create:"required" list:"user" index:"true"`
- Topic string `width:"128" nullable:"true" create:"optional" list:"user"`
- Priority string `width:"16" nullable:"true" create:"optional" list:"user"`
- Msg string `create:"required"`
- ReceivedAt time.Time `nullable:"true" list:"user" create:"optional"`
- SendAt time.Time `nullable:"false"`
- SendBy string `width:"128" nullable:"false"`
- // ClusterID identify message with same topic, msg, priority
- ClusterID string `width:"128" charset:"ascii" primary:"true" create:"optional" list:"user" get:"user"`
-}
-
-type UserDetail struct {
- Status string
- Name string
- ReceivedAt time.Time
-}
-
-func (manager *SNotificationManager) FetchCustomizeColumns(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- objs []interface{},
- fields stringutils2.SSortedStrings,
- isList bool,
-) []api.NotificationDetails {
- rows := make([]api.NotificationDetails, len(objs))
-
- resRows := manager.SStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
-
- for i := range rows {
- rows[i] = api.NotificationDetails{
- ResourceBaseDetails: resRows[i],
- }
- rows[i], _ = objs[i].(*SNotification).getMoreDetails(ctx, query, rows[i])
- }
-
- return rows
-}
-
-func (self *SNotification) GetExtraDetails(
- ctx context.Context,
- userCred mcclient.TokenCredential,
- query jsonutils.JSONObject,
- isList bool,
-) (api.NotificationDetails, error) {
- return api.NotificationDetails{}, nil
-}
-
-func (self *SNotification) getMoreDetails(ctx context.Context, query jsonutils.JSONObject, out api.NotificationDetails) (api.NotificationDetails, error) {
- var err error
-
- var scopeStr string
- scopeStr, err = query.GetString("scope")
- if err != nil {
- scopeStr = "system"
- }
- scope := rbacutils.TRbacScope(scopeStr)
-
- var userDetails []UserDetail
- if scope.HigherEqual(rbacutils.ScopeSystem) {
- userDetails, err = NotificationManager.fetchUserDetailByClusterID(ctx, self.ClusterID)
- if err != nil {
- return out, errors.Wrap(err, "NotificationManager.fetchUserDetailByClusterID")
- }
- } else {
- userDetail := UserDetail{
- Status: self.Status,
- Name: self.UID,
- ReceivedAt: self.ReceivedAt,
- }
- name, err := utils.GetUsernameByID(ctx, self.UID)
- if err == nil && len(name) != 0 {
- userDetail.Name = name
- }
- userDetails = []UserDetail{userDetail}
- }
-
- out.UserList = jsonutils.Marshal(userDetails)
- return out, nil
-}
-
-func (self *SNotificationManager) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
- return db.IsAdminAllowList(userCred, self)
-}
-
-func (self *SNotificationManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
- return db.IsAdminAllowCreate(userCred, self)
-}
-
-type sUpdate struct {
- ID string
- UID string
- Topic string
- Priority string
- ContactType string
-}
-
-func (self *SNotificationManager) InitializeData() error {
- scope := time.Duration(options.Options.InitNotificationScope) * time.Hour
- time := time.Now().Add(-scope)
- q := self.Query("id", "uid", "topic", "priority", "contact_type").GE("created_at",
- time).Desc("received_at").Equals("contact_type", "webconsole")
- q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("cluster_id")), sqlchemy.IsEmpty(q.Field("cluster_id"))))
- rows, err := q.Rows()
- if err != nil {
- return err
- }
- updates, update := make([]sUpdate, 0, 10), sUpdate{}
- for rows.Next() {
- err := rows.Scan(&update.ID, &update.UID, &update.Topic, &update.Priority, &update.ContactType)
- if err == nil {
- updates = append(updates, update)
- }
- }
- log.Debugf("this is total %d updates", len(updates))
-
- // updates is too little
- //if len(updates) < 100 {
- // updates = updates[:0]
- // q := self.Query("id", "uid", "topic", "priority", "contact_type").Desc("received_at").Equals("contact_type",
- // "webconsole").Limit(500)
- // q = q.Filter(sqlchemy.OR(sqlchemy.IsNull(q.Field("cluster_id")), sqlchemy.IsEmpty(q.Field("cluster_id"))))
- // rows, err := q.Rows()
- // if err != nil {
- // return err
- // }
- // for rows.Next() {
- // err := rows.Scan(&update.ID, &update.UID, &update.Topic, &update.Priority, &update.ContactType)
- // if err == nil {
- // updates = append(updates, update)
- // }
- // }
- // log.Debugf("this is total %d updates", len(updates))
- //}
-
- cache := make([]string, 0, 10)
- if len(updates) > 0 {
- cache = append(cache, updates[0].ID)
- }
- for i := 1; i < len(updates); i++ {
- if updates[i].Topic == updates[i-1].Topic && updates[i].Priority == updates[i-1].Priority {
-
- cache = append(cache, updates[i].ID)
- continue
- }
- err = self.syncDatabase(cache)
- if err != nil {
- return errors.Wrap(err, "exec sql error")
- }
- cache = cache[:0]
- if i < len(updates)-1 {
- cache = append(cache, updates[i].ID)
- }
- }
- if len(cache) == 0 {
- return nil
- }
- err = self.syncDatabase(cache)
- if err != nil {
- return errors.Wrap(err, "exec sql error")
- }
-
- return nil
-}
-
-func (self *SNotificationManager) syncDatabase(ids []string) error {
-
- sql := "update %s set updated_at=update_at, deleted=is_deleted, cluster_id='%s' where id in %s"
-
- newUid := DefaultUUIDGenerator()
-
- buffer := new(strings.Builder)
- buffer.WriteString("(")
- for _, id := range ids {
- buffer.WriteString("'")
- buffer.WriteString(id)
- buffer.WriteString("', ")
- }
- newSql := fmt.Sprintf(sql, self.TableSpec().Name(), newUid, buffer.String()[:buffer.Len()-2]+")")
- q := sqlchemy.NewRawQuery(newSql)
- rows, err := q.Rows()
- defer rows.Close()
- return err
-}
-
-// 通知消息列表
-func (self *SNotificationManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential,
- input api.NotificationListInput) (*sqlchemy.SQuery, error) {
-
- // no domainID for now
- scopeStr := "system"
- if len(input.Scope) > 0 {
- scopeStr = input.Scope
- }
- scope := rbacutils.TRbacScope(scopeStr)
-
- if !scope.HigherEqual(rbacutils.ScopeSystem) {
- q = q.Equals("uid", userCred.GetUserId())
- }
-
- if len(input.ContactType) > 0 {
- q = q.Equals("contact_type", input.ContactType)
- }
-
- q = q.GroupBy("cluster_id").Desc("received_at")
- return q, nil
-}
-
-func (self *SNotificationManager) BatchCreate(ctx context.Context, data jsonutils.JSONObject, contacts []SContact) ([]string, error) {
- userCred := policy.FetchUserCredential(ctx)
- ownerID, err := utils.FetchOwnerId(ctx, NotificationManager, userCred, jsonutils.JSONNull)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- msg, _ := data.GetString("msg")
- priority, _ := data.GetString("priority")
- topic, _ := data.GetString("topic")
- createFailed, createSuccess, contactSuccess := make([]string, 0), make([]*SNotification, 0, len(contacts)/2), make([]string, 0, len(contacts)/2)
-
- now, clusterId := time.Now(), DefaultUUIDGenerator()
- for i := range contacts {
- createData := map[string]interface{}{
- "uid": contacts[i].UID,
- "contact_type": contacts[i].ContactType,
- "topic": topic,
- "priority": priority,
- "msg": msg,
- "received_at": now,
- "send_by": userCred.GetUserId(),
- "status": NOTIFY_RECEIVED,
- "cluster_id": clusterId,
- }
- model, err := db.DoCreate(self, ctx, userCred, jsonutils.JSONNull, jsonutils.Marshal(createData), ownerID)
- if err != nil {
- createFailed = append(createFailed, contacts[i].ID)
- } else {
- createSuccess = append(createSuccess, model.(*SNotification))
- contactSuccess = append(contactSuccess, contacts[i].Contact)
- }
- }
- Send(createSuccess, userCred, contactSuccess)
- if len(createFailed) != 0 {
- errInfo := new(bytes.Buffer)
- errInfo.WriteString("notifications whose uid are ")
- for i := range createFailed {
- errInfo.WriteString(createFailed[i])
- errInfo.WriteString(", ")
- }
- errInfo.Truncate(errInfo.Len() - 2)
- errInfo.WriteString("created failed.")
- log.Errorf(errInfo.String())
- return nil, errors.Error("Not all notifications were sent successfully")
- }
- notificationIDs := make([]string, len(createSuccess))
- for i := range createSuccess {
- notificationIDs[i] = createSuccess[i].ID
- }
- return notificationIDs, nil
-}
-
-func (self *SNotificationManager) fetchUserDetailByClusterID(ctx context.Context, clusterID string) ([]UserDetail,
- error) {
- q := self.Query("uid", "status", "received_at").Equals("cluster_id", clusterID)
- row, err := q.Rows()
- if err != nil {
- return nil, err
- }
- ret := make([]UserDetail, 0)
- userIds := make([]string, 0)
- var userId, status string
- var receviedAt time.Time
- for row.Next() {
- err := row.Scan(&userId, &status, &receviedAt)
- if err != nil {
- return nil, errors.Wrap(err, "sql.row parse error")
- }
- userIds = append(userIds, userId)
- ret = append(ret, UserDetail{
- Status: status,
- Name: userId,
- ReceivedAt: receviedAt,
- })
- }
-
- userMap, err := cache.UserCacheManager.FetchUsersByIDs(ctx, userIds)
- if err != nil {
- return nil, errors.Wrap(err, "fetch users by ids failed")
- }
- for i := range ret {
- if user, ok := userMap[ret[i].Name]; ok {
- ret[i].Name = user.Name
- }
- }
-
- return ret, nil
-}
-
-func (self *SNotificationManager) FetchFailed(lastTime time.Time) ([]SNotification, error) {
- q := self.Query()
- q.Filter(sqlchemy.AND(sqlchemy.GE(q.Field("created_at"), lastTime), sqlchemy.Equals(q.Field("status"), NOTIFY_FAIL)))
- records := make([]SNotification, 0, 10)
- err := db.FetchModelObjects(self, q, &records)
- if err != nil {
- return nil, err
- }
- return records, nil
-}
-
-func (self *SNotification) SetStatus(userCred mcclient.TokenCredential, status string, reason string) error {
- if self.Status == status {
- return nil
- }
- oldStatus := self.Status
- _, err := db.Update(self, func() error {
- self.Status = status
- return nil
- })
- if err != nil {
- return err
- }
- if userCred != nil {
- notes := fmt.Sprintf("%s=>%s", oldStatus, status)
- if len(reason) > 0 {
- notes = fmt.Sprintf("%s: %s", notes, reason)
- }
- db.OpsLog.LogEvent(self, db.ACT_UPDATE_STATUS, notes, userCred)
- }
- return nil
-}
-
-func (self *SNotification) SetSentAndTime(userCred mcclient.TokenCredential) error {
- status := NOTIFY_SENT
- if self.Status == status {
- return nil
- }
- oldStatus := self.Status
- _, err := db.Update(self, func() error {
- self.Status = status
- self.SendAt = time.Now()
- return nil
- })
- if err != nil {
- return err
- }
- reason := "sent notification"
- if userCred != nil {
- notes := fmt.Sprintf("%s=>%s", oldStatus, status)
- if len(reason) > 0 {
- notes = fmt.Sprintf("%s: %s", notes, reason)
- }
- db.OpsLog.LogEvent(self, db.ACT_UPDATE_STATUS, notes, userCred)
- }
- return nil
-}
-
-func (self *SNotification) SetStatusWithoutUserCred(status string) error {
- _, err := db.Update(self, func() error {
- self.Status = status
- return nil
- })
- if err != nil {
- return err
- }
- return nil
-}
-func sendWithoutUserCred(notifications []SNotification) {
- // limit the number of Concurrency
- Max := 10
- limit := make(chan struct{}, Max)
- sendone := func(notification SNotification) {
- defer func() {
- <-limit
- }()
- // Get contact
- contact, err := ContactManager.FetchByUIDAndCType(notification.UID, []string{notification.ContactType})
- if err != nil {
- log.Debugf("fail to fetch contacts with uid '%s' in ReSend Cron Job", notification.UID)
- return
- }
- if len(contact) == 0 {
- return
- }
- // sent_at update todo
- notification.SetStatusWithoutUserCred(NOTIFY_SENT)
- err = NotifyService.Send(context.Background(), notification.ContactType, contact[0].Contact, notification.Topic,
- notification.Msg,
- notification.Priority)
- if err == nil {
- return
- }
- if err != nil {
- log.Errorf("Send notification failed in ReSend Cron Job: %s.", err.Error())
- notification.SetStatusWithoutUserCred(NOTIFY_FAIL)
- } else {
- notification.SetStatusWithoutUserCred(NOTIFY_OK)
- }
- }
- for i := range notifications {
- limit <- struct{}{}
- go sendone(notifications[i])
- }
- // wait all finish
- for i := 0; i < Max; i++ {
- limit <- struct{}{}
- }
-}
-
-func ReSend(seconds int) {
- scope := time.Duration(seconds+30) * time.Second
- notifications, err := NotificationManager.FetchFailed(time.Now().Add(-scope))
- if err != nil {
- return
- }
- log.Debugf("Start to resend message with a total of %d", len(notifications))
- sendWithoutUserCred(notifications)
-}
diff --git a/pkg/notify/models/mod_verify.go b/pkg/notify/models/mod_verify.go
deleted file mode 100644
index f9292dbdf6..0000000000
--- a/pkg/notify/models/mod_verify.go
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 models
-
-import (
- "context"
- "fmt"
- "time"
-
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/sqlchemy"
-
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/notify/utils"
-)
-
-type SVerifyManager struct {
- SStatusStandaloneResourceBaseManager
-}
-
-var VerifyManager *SVerifyManager
-
-func init() {
- VerifyManager = &SVerifyManager{
- SStatusStandaloneResourceBaseManager: NewStatusStandaloneResourceBaseManager(
- SVerify{},
- "notify_t_verify",
- "verification",
- "verifications",
- ),
- }
- VerifyManager.SetVirtualObject(VerifyManager)
-}
-
-type SVerify struct {
- SStatusStandaloneResourceBase
-
- CID string `width:"128" nullable:"false" create:"required" list:"user"`
- Token string `width:"200" nullable:"false" create:"required" list:"user"`
- SendAt time.Time `nullable:"true" create:"optional"`
- ExpireAt time.Time `create:"required" list:"user"`
-}
-
-// NewSVerify Generate a SVerify instance which implement a Verification Token.
-func NewSVerify(contactType string, cid string) *SVerify {
- var token string
- var expireAt time.Time
- now := time.Now()
- if contactType == EMAIL {
- token = utils.GenerateEmailToken(32)
- expireAt = now.Add(12 * time.Hour)
- } else {
- token = utils.GenerateMobileToken()
- expireAt = now.Add(2 * time.Minute)
- }
- ret := &SVerify{
- CID: cid,
- Token: token,
- ExpireAt: expireAt,
- SendAt: now,
- }
- ret.ID = DefaultUUIDGenerator()
- ret.SetModelManager(VerifyManager, ret)
- return ret
-}
-
-func (self *SVerifyManager) InitializeData() error {
- q := self.Query()
- q = q.Filter(sqlchemy.OR(sqlchemy.IsNotNull(q.Field("updated_at")), sqlchemy.IsTrue(q.Field("deleted"))))
- n, err := q.CountWithError()
- if err != nil {
- return err
- }
- if n > 0 {
- log.Debugf("no need to init data for %s", self.TableSpec().Name())
- // no need to init data
- return nil
- }
- log.Debugf("need to init data for %s", self.TableSpec().Name())
- sql := fmt.Sprintf("update %s set updated_at=update_at, deleted=is_deleted", self.TableSpec().Name())
- q = sqlchemy.NewRawQuery(sql, "")
- q.Row()
- return nil
-}
-
-func (self *SVerifyManager) FetchByCID(cid string, filter func(q *sqlchemy.SQuery) *sqlchemy.SQuery) ([]SVerify, error) {
- q := self.Query()
- q.Filter(sqlchemy.Equals(q.Field("cid"), cid))
- q = filter(q)
- records := make([]SVerify, 0, 1)
- err := db.FetchModelObjects(self, q, &records)
- if err != nil {
- return nil, err
- }
- return records, nil
-}
-
-func (self *SVerifyManager) FetchByID(id string) ([]SVerify, error) {
- q := self.Query()
- q.Filter(sqlchemy.Equals(q.Field("id"), id))
- records := make([]SVerify, 0, 1)
- err := db.FetchModelObjects(self, q, &records)
- if err != nil {
- return nil, err
- }
- return records, nil
-}
-
-func (self *SVerifyManager) Create(ctx context.Context, userCred mcclient.TokenCredential, verify *SVerify) error {
- data := jsonutils.Marshal(verify)
- ownerID, err := utils.FetchOwnerId(ctx, self, userCred, data)
- if err != nil {
- return err
- }
- _, err = db.DoCreate(self, ctx, userCred, jsonutils.JSONNull, data, ownerID)
- if err != nil {
- return err
- }
- return nil
-}
diff --git a/pkg/notify/models/notification.go b/pkg/notify/models/notification.go
new file mode 100644
index 0000000000..2de8324252
--- /dev/null
+++ b/pkg/notify/models/notification.go
@@ -0,0 +1,419 @@
+// Copyright 2019 Yunion
+//
+// 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 models
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "time"
+
+ "yunion.io/x/jsonutils"
+ "yunion.io/x/log"
+ "yunion.io/x/pkg/errors"
+ "yunion.io/x/pkg/util/sets"
+ "yunion.io/x/sqlchemy"
+
+ api "yunion.io/x/onecloud/pkg/apis/notify"
+ "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/mcclient"
+ notifyv2 "yunion.io/x/onecloud/pkg/notify"
+ "yunion.io/x/onecloud/pkg/notify/oldmodels"
+ "yunion.io/x/onecloud/pkg/notify/options"
+ "yunion.io/x/onecloud/pkg/util/rbacutils"
+ "yunion.io/x/onecloud/pkg/util/stringutils2"
+)
+
+type SNotificationManager struct {
+ db.SStatusStandaloneResourceBaseManager
+}
+
+var NotificationManager *SNotificationManager
+var NotifyService notifyv2.INotifyService
+
+func init() {
+ NotificationManager = &SNotificationManager{
+ SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager(
+ SNotification{},
+ "notifications_tbl",
+ "notification",
+ "notifications",
+ ),
+ }
+ NotificationManager.SetVirtualObject(NotificationManager)
+}
+
+type SNotification struct {
+ db.SStatusStandaloneResourceBase
+
+ ContactType string `width:"16" nullable:"false" create:"required" list:"user" get:"user" index:"true"`
+ // swagger:ignore
+ Topic string `width:"128" nullable:"true" create:"required"`
+ Priority string `width:"16" nullable:"true" create:"optional" list:"user" get:"user"`
+ // swagger:ignore
+ Message string `create:"required"`
+ ReceivedAt time.Time `nullable:"true" list:"user" get:"user"`
+ SendTimes int
+}
+
+func (nm *SNotificationManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.NotificationCreateInput) (api.NotificationCreateInput, error) {
+ // check uids, rids and contacts
+ if len(input.Receivers) == 0 && len(input.Contacts) == 0 {
+ return input, httperrors.NewMissingParameterError("receivers | contacts")
+ }
+ // check receivers
+ if len(input.Receivers) > 0 {
+ receivers, err := ReceiverManager.FetchByIdOrNames(ctx, input.Receivers...)
+ if err != nil {
+ return input, errors.Wrap(err, "ReceiverManager.FetchByIDs")
+ }
+ idSet := sets.NewString()
+ nameSet := sets.NewString()
+ for i := range receivers {
+ idSet.Insert(receivers[i].Id)
+ nameSet.Insert(receivers[i].Name)
+ }
+ for _, re := range input.Receivers {
+ if idSet.Has(re) || nameSet.Has(re) {
+ continue
+ }
+ return input, httperrors.NewInputParameterError("no such receiver whose uid is %q", re)
+ }
+ input.Receivers = idSet.UnsortedList()
+ }
+ nowStr := time.Now().Format("2006-01-02 15:04:05")
+ // hack
+ input.Name = fmt.Sprintf("%s(%s)", input.Topic, nowStr)
+ return input, nil
+}
+
+func (n *SNotification) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
+ n.ReceivedAt = time.Now()
+ n.Id = db.DefaultUUIDGenerator()
+ var input api.NotificationCreateInput
+ err := data.Unmarshal(&input)
+ if err != nil {
+ return err
+ }
+ for i := range input.Receivers {
+ _, err := ReceiverNotificationManager.Create(ctx, userCred, input.Receivers[i], n.Id)
+ if err != nil {
+ return errors.Wrap(err, "ReceiverNotificationManager.Create")
+ }
+ }
+ return nil
+}
+
+func (n *SNotification) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
+ n.SetStatus(userCred, api.NOTIFICATION_STATUS_RECEIVED, "")
+ task, err := taskman.TaskManager.NewTask(ctx, "NotificationSendTask", n, userCred, nil, "", "")
+ if err != nil {
+ log.Errorf("NotificationSendTask newTask error %v", err)
+ } else {
+ task.ScheduleRun(nil)
+ }
+}
+
+func (nm *SNotificationManager) FetchCustomizeColumns(
+ ctx context.Context,
+ userCred mcclient.TokenCredential,
+ query jsonutils.JSONObject,
+ objs []interface{},
+ fields stringutils2.SSortedStrings,
+ isList bool,
+) []api.NotificationDetails {
+ rows := make([]api.NotificationDetails, len(objs))
+
+ resRows := nm.SStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
+
+ var err error
+ for i := range rows {
+ rows[i], err = objs[i].(*SNotification).getMoreDetails(ctx, query, rows[i])
+ if err != nil {
+ log.Errorf("Notification.getMoreDetails: %v", err)
+ }
+ rows[i].StatusStandaloneResourceDetails = resRows[i]
+ }
+
+ return rows
+}
+
+func (n *SNotification) GetExtraDetails(
+ ctx context.Context,
+ userCred mcclient.TokenCredential,
+ query jsonutils.JSONObject,
+ isList bool,
+) (api.NotificationDetails, error) {
+ return api.NotificationDetails{}, nil
+}
+
+func (n *SNotification) ReceiverNotificationsNotOK() ([]SReceiverNotification, error) {
+ rnq := ReceiverNotificationManager.Query().Equals("notification_id", n.Id).NotEquals("status", api.RECEIVER_NOTIFICATION_OK)
+ rns := make([]SReceiverNotification, 0, 1)
+ err := db.FetchModelObjects(ReceiverNotificationManager, rnq, &rns)
+ if err != nil {
+ return nil, err
+ }
+ return rns, nil
+}
+
+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")))
+ ret := make([]api.ReceiveDetail, 0, 2)
+ err := q.All(&ret)
+ if err != nil && errors.Cause(err) != sql.ErrNoRows {
+ log.Errorf("SQuery.All: %v", err)
+ return nil, err
+ }
+ return ret, nil
+}
+
+func (n *SNotification) getMoreDetails(ctx context.Context, query jsonutils.JSONObject, out api.NotificationDetails) (api.NotificationDetails, error) {
+ // get title adn content
+ p, err := TemplateManager.NotifyFilter(n.ContactType, n.Topic, n.Message)
+ if err != nil {
+ return out, err
+ }
+ out.Title = p.Title
+ out.Content = p.Message
+ // get receive details
+ out.ReceiveDetails, err = n.ReceiveDetails()
+ if err != nil {
+ return out, err
+ }
+ return out, nil
+}
+
+func (nm *SNotificationManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
+ if !data.Contains("contacts") {
+ return db.IsAdminAllowCreate(userCred, nm)
+ }
+ return true
+}
+
+func (nm *SNotificationManager) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
+ return false
+}
+
+func (nm *SNotificationManager) ResourceScope() rbacutils.TRbacScope {
+ return rbacutils.ScopeUser
+}
+
+func (nm *SNotificationManager) NamespaceScope() rbacutils.TRbacScope {
+ return rbacutils.ScopeSystem
+}
+
+func (nm *SNotificationManager) FetchOwnerId(ctx context.Context, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) {
+ return db.FetchUserInfo(ctx, data)
+}
+
+func (nm *SNotificationManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery {
+ if owner == nil {
+ return q
+ }
+ switch scope {
+ case rbacutils.ScopeDomain:
+ subRq := ReceiverManager.Query("id").Equals("domain_id", owner.GetDomainId()).SubQuery()
+ subRNq := ReceiverNotificationManager.Query("notification_id").Join(subRq, sqlchemy.Equals(q.Field("receiver_id"), subRq.Field("id"))).SubQuery()
+ q = q.Join(subRNq, sqlchemy.Equals(q.Field("id"), subRNq.Field("notification_id")))
+ case rbacutils.ScopeProject, rbacutils.ScopeUser:
+ subq := ReceiverNotificationManager.Query("notification_id").Equals("receiver_id", owner.GetUserId()).SubQuery()
+ q = q.Join(subq, sqlchemy.Equals(q.Field("id"), subq.Field("notification_id")))
+ }
+ return q
+}
+
+func (n *SNotification) AddOne() error {
+ _, err := db.Update(n, func() error {
+ n.SendTimes += 1
+ return nil
+ })
+ return err
+}
+
+const (
+ NOTIFY_RECEIVED = "received" // Received a task about sending a notification
+ NOTIFY_SENT = "sent" // Nofity module has sent notification, but result unkown
+ NOTIFY_OK = "sent_ok" // Notification was sent successfully
+ NOTIFY_FAIL = "sent_fail" // That sent a notification is failed
+ NOTIFY_REMOVED = "removed"
+)
+
+func (self *SNotificationManager) singleRowLineQuery(sqlStr string, dest ...interface{}) error {
+ q := sqlchemy.NewRawQuery(sqlStr)
+ rows, err := q.Rows()
+ if err != nil {
+ return errors.Wrap(err, "q.Rows")
+ }
+ defer rows.Close()
+ for rows.Next() {
+ err := rows.Scan(dest...)
+ if err != nil {
+ return errors.Wrap(err, "rows.Scan")
+ }
+ return nil
+ }
+ return sql.ErrNoRows
+}
+
+func (self *SNotificationManager) InitializeData() error {
+ // check
+ sqlStr := fmt.Sprintf(
+ "select count(*) as total from (select cluster_id from %s where status='%s' and contact_type='webconsole' group by cluster_id) as cluster",
+ oldmodels.NotificationManager.TableSpec().Name(),
+ NOTIFY_REMOVED,
+ )
+ var count int
+ err := self.singleRowLineQuery(sqlStr, &count)
+ if err != nil {
+ return err
+ }
+ if count >= options.Options.MaxSyncNotification {
+ return nil
+ }
+
+ limitTimeStr := time.Now().Add(time.Duration(-30) * time.Hour * 24).Format("2006-01-02 15:04:05")
+
+ // get min received_at
+ var minReceivedAt time.Time
+ sqlStr = fmt.Sprintf(
+ "select min(received_at) as min_received_at from (select received_at from %s where received_at > '%s' and contact_type = 'webconsole' group by cluster_id order by received_at desc limit %d) as cluster",
+ oldmodels.NotificationManager.TableSpec().Name(),
+ limitTimeStr,
+ options.Options.MaxSyncNotification,
+ )
+ err = self.singleRowLineQuery(sqlStr, &minReceivedAt)
+ if err != nil {
+ return err
+ }
+ log.Infof("minReceivedAt: %s", minReceivedAt)
+
+ ctx := context.Background()
+ q := oldmodels.NotificationManager.Query().Equals("contact_type", api.WEBCONSOLE).GT("received_at", minReceivedAt).NotEquals("status", NOTIFY_REMOVED)
+ n := q.Count()
+ log.Infof("total %d notifications to sync", n)
+ oldNotifications := make([]oldmodels.SNotification, 0, n)
+ err = db.FetchModelObjects(oldmodels.NotificationManager, q, &oldNotifications)
+ if err != nil {
+ return errors.Wrap(err, "db.FetchModelObjects")
+ }
+
+ // build cluster=>Notification
+ cnMap := make(map[string][]*oldmodels.SNotification)
+ for i := range oldNotifications {
+ clusterId := oldNotifications[i].ClusterID
+ if _, ok := cnMap[clusterId]; !ok {
+ cnMap[clusterId] = make([]*oldmodels.SNotification, 0, 2)
+ }
+ cnMap[clusterId] = append(cnMap[clusterId], &oldNotifications[i])
+ }
+
+ for _, oldNotifications := range cnMap {
+ oldNotificaion := oldNotifications[0]
+ newNotification := SNotification{
+ ContactType: oldNotificaion.ContactType,
+ Topic: oldNotificaion.Topic,
+ Priority: oldNotificaion.Priority,
+ Message: oldNotificaion.Msg,
+ ReceivedAt: oldNotificaion.ReceivedAt,
+ }
+ newNotification.Id = db.DefaultUUIDGenerator()
+ statusMap := make(map[string]int, 4)
+ for _, oldNotificaion := range oldNotifications {
+ rn := SReceiverNotification{
+ ReceiverID: oldNotificaion.UID,
+ NotificationID: newNotification.Id,
+ SendAt: oldNotificaion.SendAt,
+ SendBy: oldNotificaion.SendBy,
+ Status: oldNotificaion.Status,
+ }
+ if rn.Status == NOTIFY_SENT {
+ rn.Status = api.NOTIFICATION_STATUS_SENDING
+ }
+ statusMap[rn.Status] += 1
+ err := ReceiverNotificationManager.TableSpec().Insert(ctx, &rn)
+ if err != nil {
+ return errors.Wrap(err, "TableSpec().Insert")
+ }
+ }
+ switch {
+ case statusMap[api.RECEIVER_NOTIFICATION_OK] == len(oldNotifications):
+ newNotification.Status = api.NOTIFICATION_STATUS_OK
+ case statusMap[api.RECEIVER_NOTIFICATION_RECEIVED] == len(oldNotifications):
+ newNotification.Status = api.NOTIFICATION_STATUS_RECEIVED
+ case statusMap[api.RECEIVER_NOTIFICATION_FAIL] == len(oldNotifications):
+ newNotification.Status = api.NOTIFICATION_STATUS_FAILED
+ case statusMap[api.RECEIVER_NOTIFICATION_FAIL] == 0 && statusMap[api.RECEIVER_NOTIFICATION_SENT] > 0:
+ newNotification.Status = api.NOTIFICATION_STATUS_SENDING
+ default:
+ newNotification.Status = api.NOTIFICATION_STATUS_PART_OK
+ }
+ err := self.TableSpec().InsertOrUpdate(ctx, &newNotification)
+ if err != nil {
+ return errors.Wrap(err, "TableSpec().InsertOrUpdate")
+ }
+
+ // mark removed
+ for _, oldNotificaion := range oldNotifications {
+ _, err := db.Update(oldNotificaion, func() error {
+ oldNotificaion.Status = NOTIFY_REMOVED
+ return nil
+ })
+ if err != nil {
+ return errors.Wrap(err, "Delete")
+ }
+ }
+ }
+ return nil
+}
+
+// 通知消息列表
+func (nm *SNotificationManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.NotificationListInput) (*sqlchemy.SQuery, error) {
+ q, err := nm.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StandaloneResourceListInput)
+ if err != nil {
+ return nil, err
+ }
+ if len(input.ContactType) > 0 {
+ q = q.Equals("contact_type", input.ContactType)
+ }
+ if len(input.ReceiverId) > 0 {
+ subq := ReceiverNotificationManager.Query("notification_id").Equals("receiver_id", input.ReceiverId).SubQuery()
+ q = q.Join(subq, sqlchemy.Equals(q.Field("id"), subq.Field("notification_id")))
+ }
+ return q, nil
+}
+
+func (nm *SNotificationManager) ReSend(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
+ q := nm.Query().NotEquals("status", api.NOTIFICATION_STATUS_OK).LT("send_times", options.Options.MaxSendTimes)
+ ns := make([]SNotification, 0, 2)
+ err := db.FetchModelObjects(nm, q, &ns)
+ if err != nil {
+ log.Errorf("fail to FetchModelObjects: %v", err)
+ return
+ }
+ for i := range ns {
+ task, err := taskman.TaskManager.NewTask(ctx, "NotificationSendTask", &ns[i], userCred, nil, "", "")
+ if err != nil {
+ log.Errorf("NotificationSendTask newTask error %v", err)
+ } else {
+ task.ScheduleRun(nil)
+ }
+ }
+}
diff --git a/pkg/notify/models/receiver.go b/pkg/notify/models/receiver.go
new file mode 100644
index 0000000000..97d634af78
--- /dev/null
+++ b/pkg/notify/models/receiver.go
@@ -0,0 +1,884 @@
+// Copyright 2019 Yunion
+//
+// 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 models
+
+import (
+ "context"
+
+ "yunion.io/x/jsonutils"
+ "yunion.io/x/log"
+ "yunion.io/x/pkg/errors"
+ "yunion.io/x/pkg/tristate"
+ "yunion.io/x/pkg/util/regutils"
+ "yunion.io/x/pkg/util/sets"
+ "yunion.io/x/pkg/utils"
+ "yunion.io/x/sqlchemy"
+
+ "yunion.io/x/onecloud/pkg/apis"
+ api "yunion.io/x/onecloud/pkg/apis/notify"
+ "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/mcclient"
+ "yunion.io/x/onecloud/pkg/mcclient/auth"
+ "yunion.io/x/onecloud/pkg/mcclient/informer"
+ "yunion.io/x/onecloud/pkg/mcclient/modules"
+ "yunion.io/x/onecloud/pkg/notify/oldmodels"
+ "yunion.io/x/onecloud/pkg/util/httputils"
+ "yunion.io/x/onecloud/pkg/util/logclient"
+ "yunion.io/x/onecloud/pkg/util/stringutils2"
+)
+
+var (
+ AllContactTypes = []string{
+ api.EMAIL,
+ api.MOBILE,
+ api.DINGTALK,
+ api.FEISHU,
+ api.WORKWX,
+ api.WEBCONSOLE,
+ }
+ AllSubContactTypes = []string{
+ api.DINGTALK,
+ api.FEISHU,
+ api.WORKWX,
+ api.WEBCONSOLE,
+ }
+ AllRobotContactTypes = []string{
+ api.FEISHU_ROBOT,
+ api.DINGTALK_ROBOT,
+ api.WORKWX_ROBOT,
+ }
+)
+
+type SReceiverManager struct {
+ db.SStatusStandaloneResourceBaseManager
+ db.SDomainizedResourceBaseManager
+ db.SEnabledResourceBaseManager
+}
+
+var ReceiverManager *SReceiverManager
+
+func init() {
+ ReceiverManager = &SReceiverManager{
+ SStatusStandaloneResourceBaseManager: db.NewStatusStandaloneResourceBaseManager(
+ SReceiver{},
+ "receivers_tbl",
+ "receiver",
+ "receivers",
+ ),
+ }
+ ReceiverManager.SetVirtualObject(ReceiverManager)
+}
+
+type SReceiver struct {
+ db.SStatusStandaloneResourceBase
+ db.SDomainizedResourceBase
+ db.SEnabledResourceBase
+
+ Email string `width:"64" nullable:"false" create:"optional" update:"user" get:"user" list:"admin"`
+ Mobile string `width:"16" nullable:"false" create:"optional" update:"user" get:"user" list:"admin"`
+
+ // swagger:ignore
+ EnabledEmail tristate.TriState `nullable:"false" default:"false" update:"user"`
+ // swagger:ignore
+ VerifiedEmail tristate.TriState `nullable:"false" default:"false" update:"user"`
+
+ // swagger:ignore
+ EnabledMobile tristate.TriState `nullable:"false" default:"false" update:"user"`
+ // swagger:ignore
+ VerifiedMobile tristate.TriState `nullable:"false" default:"false" update:"user"`
+
+ // swagger:ignore
+ subContactCache map[string]*SSubContact `json:"-"`
+}
+
+func (rm *SReceiverManager) InitializeData() error {
+ ctx := context.Background()
+ userCred := auth.AdminCredential()
+ log.Infof("Init Receiver...")
+ // Fetch all old SContact
+ q := oldmodels.ContactManager.Query()
+ contacts := make([]oldmodels.SContact, 0, 50)
+ err := db.FetchModelObjects(oldmodels.ContactManager, q, &contacts)
+ if err != nil {
+ return errors.Wrap(err, "db.FetchModelObjects")
+ }
+
+ // build uid map
+ uids := make([]string, 0, 10)
+ contactMap := make(map[string][]*oldmodels.SContact, 10)
+ for i := range contacts {
+ uid := contacts[i].UID
+ if _, ok := contactMap[uid]; !ok {
+ contactMap[uid] = make([]*oldmodels.SContact, 0, 4)
+ uids = append(uids, uid)
+ }
+ contactMap[uid] = append(contactMap[uid], &contacts[i])
+ }
+
+ // build uid->uname map
+ userMap, err := oldmodels.UserCacheManager.FetchUsersByIDs(context.Background(), uids)
+ if err != nil {
+ return errors.Wrap(err, "oldmodels.UserCacheManager.FetchUsersByIDs")
+ }
+
+ // build Receivers
+ for uid, contacts := range contactMap {
+ var receiver SReceiver
+ receiver.subContactCache = make(map[string]*SSubContact)
+ receiver.Enabled = tristate.True
+ receiver.Status = api.RECEIVER_STATUS_READY
+ receiver.Id = uid
+ user, ok := userMap[uid]
+ if !ok {
+ log.Errorf("no user %q in usercache", uid)
+ } else {
+ receiver.Name = user.Name
+ receiver.DomainId = user.DomainId
+ }
+ webconsole := false
+ for _, contact := range contacts {
+ switch contact.ContactType {
+ case api.EMAIL:
+ receiver.Email = contact.Contact
+ if contact.Enabled == "1" {
+ receiver.EnabledEmail = tristate.True
+ } else {
+ receiver.EnabledEmail = tristate.False
+ }
+ if contact.Status == oldmodels.CONTACT_VERIFIED {
+ receiver.VerifiedEmail = tristate.True
+ } else {
+ receiver.VerifiedEmail = tristate.False
+ }
+ case api.MOBILE:
+ receiver.Mobile = contact.Contact
+ if contact.Enabled == "1" {
+ receiver.EnabledMobile = tristate.True
+ } else {
+ receiver.EnabledMobile = tristate.False
+ }
+ if contact.Status == oldmodels.CONTACT_VERIFIED {
+ receiver.VerifiedMobile = tristate.True
+ } else {
+ receiver.VerifiedMobile = tristate.False
+ }
+ default:
+ var subContact SSubContact
+ subContact.Type = contact.ContactType
+ if subContact.Type == api.WEBCONSOLE {
+ webconsole = true
+ subContact.Contact = uid
+ } else {
+ subContact.Contact = contact.Contact
+ }
+ subContact.ReceiverID = uid
+ subContact.ParentContactType = api.MOBILE
+ if contact.Enabled == "1" {
+ subContact.Enabled = tristate.True
+ } else {
+ subContact.Enabled = tristate.False
+ }
+ if contact.Status == oldmodels.CONTACT_VERIFIED && len(contact.Contact) > 0 {
+ subContact.Verified = tristate.True
+ } else {
+ subContact.Verified = tristate.False
+ }
+ receiver.subContactCache[contact.ContactType] = &subContact
+ }
+ }
+ if !webconsole {
+ receiver.subContactCache[api.WEBCONSOLE] = &SSubContact{
+ ReceiverID: receiver.Id,
+ Type: api.WEBCONSOLE,
+ Contact: receiver.Id,
+ ParentContactType: "",
+ Enabled: tristate.True,
+ Verified: tristate.True,
+ }
+ }
+ err := rm.TableSpec().InsertOrUpdate(ctx, &receiver)
+ if err != nil {
+ return errors.Wrap(err, "InsertOrUpdate")
+ }
+ err = receiver.PushCache(ctx)
+ if err != nil {
+ return errors.Wrap(err, "PushCache")
+ }
+ //delete old one
+ for _, contact := range contacts {
+ err := contact.Delete(ctx, userCred)
+ if err != nil {
+ return errors.Wrap(err, "Delete")
+ }
+ }
+ }
+ return nil
+}
+
+func (rm *SReceiverManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.ReceiverCreateInput) (api.ReceiverCreateInput, error) {
+ var err error
+ input.StatusStandaloneResourceCreateInput, err = rm.SStatusStandaloneResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.StatusStandaloneResourceCreateInput)
+ if err != nil {
+ return input, err
+ }
+ // check uid
+ session := auth.GetSession(ctx, userCred, "", "")
+ if len(input.UID) > 0 {
+ userObj, err := modules.UsersV3.GetById(session, input.UID, nil)
+ if err != nil {
+ if jErr, ok := err.(*httputils.JSONClientError); ok {
+ if jErr.Code == 404 {
+ return input, httperrors.NewInputParameterError("no such user")
+ }
+ }
+ return input, err
+ }
+ uname, _ := userObj.GetString("name")
+ input.UName = uname
+ domainId, _ := userObj.GetString("domain_id")
+ input.ProjectDomainId = domainId
+ } else {
+ if len(input.UName) == 0 {
+ return input, httperrors.NewMissingParameterError("uid or uname")
+ } else {
+ userObj, err := modules.UsersV3.GetByName(session, input.UName, nil)
+ if err != nil {
+ if jErr, ok := err.(*httputils.JSONClientError); ok {
+ if jErr.Code == 404 {
+ return input, httperrors.NewInputParameterError("no such user")
+ }
+ }
+ return input, err
+ }
+ uid, _ := userObj.GetString("id")
+ input.UID = uid
+ domainId, _ := userObj.GetString("domain_id")
+ input.ProjectDomainId = domainId
+ }
+ }
+ // hack
+ input.Name = input.UName
+ // validate email
+ if ok := regutils.MatchEmail(input.Email); !ok {
+ return input, httperrors.NewInputParameterError("invalid email")
+ }
+ // validate mobile
+ if ok := regutils.MatchMobile(input.Mobile); !ok {
+ return input, httperrors.NewInputParameterError("invalid mobile")
+ }
+ return input, nil
+}
+
+func (r *SReceiver) IsEnabledContactType(ct string) (bool, error) {
+ if utils.IsInStringArray(ct, AllRobotContactTypes) {
+ return true, nil
+ }
+ cts, err := r.GetEnabledContactTypes()
+ if err != nil {
+ return false, errors.Wrap(err, "GetEnabledContactTypes")
+ }
+ return utils.IsInStringArray(ct, cts), nil
+}
+
+func (r *SReceiver) IsVerifiedContactType(ct string) (bool, error) {
+ if utils.IsInStringArray(ct, AllRobotContactTypes) {
+ return true, nil
+ }
+ cts, err := r.GetVerifiedContactTypes()
+ if err != nil {
+ return false, errors.Wrap(err, "GetVerifiedContactTypes")
+ }
+ return utils.IsInStringArray(ct, cts), nil
+}
+
+func (r *SReceiver) GetEnabledContactTypes() ([]string, error) {
+ if err := r.PullCache(false); err != nil {
+ return nil, err
+ }
+ ret := make([]string, 0, 1)
+ // for email and mobile
+ if r.EnabledEmail.IsTrue() {
+ ret = append(ret, api.EMAIL)
+ }
+ if r.EnabledMobile.IsTrue() {
+ ret = append(ret, api.MOBILE)
+ }
+ for subct, subc := range r.subContactCache {
+ if subc.Enabled.IsTrue() {
+ ret = append(ret, subct)
+ }
+ }
+ return ret, nil
+}
+
+func (r *SReceiver) setEnabledContactType(contactType string, enabled bool) {
+ switch contactType {
+ case api.EMAIL:
+ r.EnabledEmail = tristate.NewFromBool(enabled)
+ case api.MOBILE:
+ r.EnabledMobile = tristate.NewFromBool(enabled)
+ default:
+ if sc, ok := r.subContactCache[contactType]; ok {
+ sc.Enabled = tristate.NewFromBool(enabled)
+ } else {
+ r.subContactCache[contactType] = &SSubContact{
+ Type: contactType,
+ ReceiverID: r.Id,
+ Enabled: tristate.NewFromBool(enabled),
+ }
+ }
+ }
+}
+
+func (r *SReceiver) SetEnabledContactTypes(contactTypes []string) error {
+ if err := r.PullCache(false); err != nil {
+ return err
+ }
+ ctSet := sets.NewString(contactTypes...)
+ for _, ct := range AllContactTypes {
+ if ctSet.Has(ct) {
+ r.setEnabledContactType(ct, true)
+ } else {
+ r.setEnabledContactType(ct, false)
+ }
+ }
+ return nil
+}
+
+func (r *SReceiver) MarkContactTypeVerified(contactType string) error {
+ if err := r.PullCache(false); err != nil {
+ return err
+ }
+ if sc, ok := r.subContactCache[contactType]; ok {
+ sc.Verified = tristate.True
+ } else {
+ r.subContactCache[contactType] = &SSubContact{
+ ReceiverID: r.Id,
+ Verified: tristate.True,
+ }
+ }
+ return nil
+}
+
+func (r *SReceiver) setVerifiedContactType(contactType string, enabled bool) {
+ switch contactType {
+ case api.EMAIL:
+ r.VerifiedEmail = tristate.NewFromBool(enabled)
+ case api.MOBILE:
+ r.VerifiedMobile = tristate.NewFromBool(enabled)
+ default:
+ if sc, ok := r.subContactCache[contactType]; ok {
+ sc.Verified = tristate.NewFromBool(enabled)
+ } else {
+ r.subContactCache[contactType] = &SSubContact{
+ ReceiverID: r.Id,
+ Verified: tristate.NewFromBool(enabled),
+ }
+ }
+ }
+}
+
+func (r *SReceiver) GetVerifiedContactTypes() ([]string, error) {
+ if err := r.PullCache(false); err != nil {
+ return nil, err
+ }
+ ret := make([]string, 0, 1)
+ // for email and mobile
+ if r.VerifiedEmail.IsTrue() {
+ ret = append(ret, api.EMAIL)
+ }
+ if r.VerifiedMobile.IsTrue() {
+ ret = append(ret, api.MOBILE)
+ }
+ for subct, subc := range r.subContactCache {
+ if subc.Verified.IsTrue() {
+ ret = append(ret, subct)
+ }
+ }
+ return ret, nil
+}
+
+func (r *SReceiver) SetVerifiedContactTypes(contactTypes []string) error {
+ if err := r.PullCache(false); err != nil {
+ return err
+ }
+ ctSet := sets.NewString(contactTypes...)
+ for _, ct := range AllContactTypes {
+ if ctSet.Has(ct) {
+ r.setVerifiedContactType(ct, true)
+ } else {
+ r.setVerifiedContactType(ct, false)
+ }
+ }
+ return nil
+}
+
+func (r *SReceiver) PullCache(force bool) error {
+ if !force && r.subContactCache != nil {
+ return nil
+ }
+ cache, err := SubContactManager.fetchMapByReceiverID(r.Id)
+ if err != nil {
+ return err
+ }
+ r.subContactCache = cache
+ return nil
+}
+
+func (r *SReceiver) PushCache(ctx context.Context) error {
+ for subct, subc := range r.subContactCache {
+ err := SubContactManager.TableSpec().InsertOrUpdate(ctx, subc)
+ if err != nil {
+ return errors.Wrapf(err, "fail to save subcontact %q to db", subct)
+ }
+ }
+ return nil
+}
+
+func (rm *SReceiverManager) EnabledContactFilter(contactType string, q *sqlchemy.SQuery) *sqlchemy.SQuery {
+ subQuery := SubContactManager.Query("receiver_id").Equals("type", contactType).IsTrue("enabled").SubQuery()
+ q = q.Join(subQuery, sqlchemy.Equals(subQuery.Field("receiver_id"), q.Field("id")))
+ return q
+}
+
+func (rm *SReceiverManager) VerifiedContactFilter(contactType string, q *sqlchemy.SQuery) *sqlchemy.SQuery {
+ subQuery := SubContactManager.Query("receiver_id").Equals("type", contactType).IsTrue("verified").SubQuery()
+ q = q.Join(subQuery, sqlchemy.Equals(subQuery.Field("receiver_id"), q.Field("id")))
+ return q
+}
+
+func (rm *SReceiverManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.ReceiverListInput) (*sqlchemy.SQuery, error) {
+ q, err := rm.SStatusStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StatusStandaloneResourceListInput)
+ if err != nil {
+ return nil, err
+ }
+ q, err = rm.SDomainizedResourceBaseManager.ListItemFilter(ctx, q, userCred, input.DomainizedResourceListInput)
+ if err != nil {
+ return nil, err
+ }
+ q, err = rm.SEnabledResourceBaseManager.ListItemFilter(ctx, q, userCred, input.EnabledResourceBaseListInput)
+ if err != nil {
+ return nil, err
+ }
+ if len(input.UID) > 0 {
+ q = q.Equals("id", input.UID)
+ }
+ if len(input.UName) > 0 {
+ q = q.Equals("name", input.UName)
+ }
+ if len(input.EnabledContactType) > 0 {
+ q = rm.EnabledContactFilter(input.EnabledContactType, q)
+ }
+ if len(input.VerifiedContactType) > 0 {
+ q = rm.VerifiedContactFilter(input.VerifiedContactType, q)
+ }
+ return q, nil
+}
+
+func (rm *SReceiverManager) FetchCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []interface{}, fields stringutils2.SSortedStrings, isList bool) []api.ReceiverDetails {
+ sRows := rm.SStatusStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
+ dRows := rm.SDomainizedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
+ rows := make([]api.ReceiverDetails, len(objs))
+ var err error
+ for i := range rows {
+ rows[i].StatusStandaloneResourceDetails = sRows[i]
+ rows[i].DomainizedResourceInfo = dRows[i]
+ user := objs[i].(*SReceiver)
+ if rows[i].EnabledContactTypes, err = user.GetEnabledContactTypes(); err != nil {
+ log.Errorf("GetEnabledContactTypes: %v", err)
+ }
+ if rows[i].VerifiedContactTypes, err = user.GetVerifiedContactTypes(); err != nil {
+ log.Errorf("GetVerifiedContactTypes: %v", err)
+ }
+ }
+ return rows
+}
+
+func (rm *SReceiverManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field string) (*sqlchemy.SQuery, error) {
+ q, err := rm.SStatusStandaloneResourceBaseManager.QueryDistinctExtraField(q, field)
+ if err != nil {
+ return nil, err
+ }
+ q, err = rm.SDomainizedResourceBaseManager.QueryDistinctExtraField(q, field)
+ if err != nil {
+ return nil, err
+ }
+ return q, nil
+}
+
+func (rm *SReceiverManager) OrderByExtraFields(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query api.ReceiverListInput) (*sqlchemy.SQuery, error) {
+ q, err := rm.SStatusStandaloneResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.StatusStandaloneResourceListInput)
+ if err != nil {
+ return nil, err
+ }
+ q, err = rm.SDomainizedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.DomainizedResourceListInput)
+ return q, nil
+}
+
+func (r *SReceiver) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
+ r.SStatusStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
+ // set status
+ r.SetStatus(userCred, api.RECEIVER_STATUS_PULLING, "")
+ logclient.AddActionLogWithContext(ctx, r, logclient.ACT_CREATE, nil, userCred, true)
+ task, err := taskman.TaskManager.NewTask(ctx, "SubcontactPullTask", r, userCred, nil, "", "")
+ if err != nil {
+ log.Errorf("ContactPullTask newTask error %v", err)
+ } else {
+ task.ScheduleRun(nil)
+ }
+}
+
+func (r *SReceiver) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
+ err := r.SStatusStandaloneResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
+ if err != nil {
+ return nil
+ }
+ var input api.ReceiverCreateInput
+ err = data.Unmarshal(&input)
+ if err != nil {
+ return err
+ }
+ // set id and name
+ r.Id = input.UID
+ r.Name = input.UName
+ r.DomainId = input.ProjectDomainId
+ if input.Enabled == nil {
+ r.Enabled = tristate.True
+ }
+ err = r.SetEnabledContactTypes(input.EnabledContactTypes)
+ if err != nil {
+ return errors.Wrap(err, "SetEnabledContactTypes")
+ }
+ err = r.PushCache(ctx)
+ if err != nil {
+ return errors.Wrap(err, "PushCache")
+ }
+ return nil
+}
+
+func (r *SReceiver) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ReceiverUpdateInput) (api.ReceiverUpdateInput, error) {
+ var err error
+ input.StatusStandaloneResourceBaseUpdateInput, err = r.SStatusStandaloneResourceBase.ValidateUpdateData(ctx, userCred, query, input.StatusStandaloneResourceBaseUpdateInput)
+ if err != nil {
+ return input, err
+ }
+ // validate email
+ if ok := regutils.MatchEmail(input.Email); !ok {
+ return input, httperrors.NewInputParameterError("invalid email")
+ }
+ // validate mobile
+ if ok := regutils.MatchMobile(input.Mobile); !ok {
+ return input, httperrors.NewInputParameterError("invalid mobile")
+ }
+ return input, nil
+}
+
+func (r *SReceiver) PreUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
+ r.SStatusStandaloneResourceBase.PreUpdate(ctx, userCred, query, data)
+ var input api.ReceiverUpdateInput
+ err := data.Unmarshal(&input)
+ if err != nil {
+ log.Errorf("fail to unmarshal to ContactUpdateInput: %v", err)
+ }
+ err = r.PullCache(false)
+ if err != nil {
+ log.Errorf("PullCache: %v", err)
+ }
+ err = r.SetEnabledContactTypes(input.EnabledContactTypes)
+ if len(input.Email) != 0 {
+ r.VerifiedEmail = tristate.False
+ for _, c := range r.subContactCache {
+ if c.ParentContactType == input.Email {
+ c.Verified = tristate.False
+ }
+ }
+ }
+ if len(input.Mobile) != 0 {
+ r.VerifiedMobile = tristate.False
+ for _, c := range r.subContactCache {
+ if c.ParentContactType == input.Mobile {
+ c.Verified = tristate.False
+ }
+ }
+ }
+ err = r.PushCache(ctx)
+ if err != nil {
+ log.Errorf("PushCache: %v", err)
+ }
+ err = ReceiverManager.TableSpec().InsertOrUpdate(ctx, r)
+ if err != nil {
+ log.Errorf("InsertOrUpdate: %v", err)
+ }
+}
+
+func (r *SReceiver) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) {
+ r.SStatusStandaloneResourceBase.PostUpdate(ctx, userCred, query, data)
+ // set status
+ r.SetStatus(userCred, api.RECEIVER_STATUS_PULLING, "")
+ logclient.AddActionLogWithContext(ctx, r, logclient.ACT_UPDATE, nil, userCred, true)
+ task, err := taskman.TaskManager.NewTask(ctx, "SubcontactPullTask", r, userCred, nil, "", "")
+ if err != nil {
+ log.Errorf("ContactPullTask newTask error %v", err)
+ } else {
+ task.ScheduleRun(nil)
+ }
+}
+
+func (r *SReceiver) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
+ err := r.PullCache(false)
+ if err != nil {
+ return err
+ }
+ for _, sc := range r.subContactCache {
+ err := sc.Delete(ctx, userCred)
+ if err != nil {
+ return err
+ }
+ }
+ return r.SStatusStandaloneResourceBase.Delete(ctx, userCred)
+}
+
+func (r *SReceiver) IsOwner(userCred mcclient.TokenCredential) bool {
+ return r.Id == userCred.GetUserId()
+}
+
+func (r *SReceiver) AllowPerformTriggerVerify(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
+ return r.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, r, "trigger_verify")
+}
+
+func (r *SReceiver) PerformTriggerVerify(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ReceiverTriggerVerifyInput) (jsonutils.JSONObject, error) {
+ if len(input.ContactType) == 0 {
+ return nil, httperrors.NewMissingParameterError("contact_type")
+ }
+ if !utils.IsInStringArray(input.ContactType, []string{api.EMAIL, api.MOBILE}) {
+ return nil, httperrors.NewInputParameterError("not support such contact type %q", input.ContactType)
+ }
+ _, err := VerificationManager.Create(ctx, r.Id, input.ContactType)
+ if err == ErrVerifyFrequently {
+ return nil, httperrors.NewForbiddenError("Send verify message too frequently, please try again later")
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ params := jsonutils.Marshal(input).(*jsonutils.JSONDict)
+ task, err := taskman.TaskManager.NewTask(ctx, "VerificationSendTask", r, userCred, params, "", "")
+ if err != nil {
+ log.Errorf("ContactPullTask newTask error %v", err)
+ } else {
+ task.ScheduleRun(nil)
+ }
+ return nil, nil
+}
+
+func (r *SReceiver) AllowPerformVerify(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
+ return r.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, r, "verify")
+}
+
+func (r *SReceiver) PerformVerify(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ReceiverVerifyInput) (jsonutils.JSONObject, error) {
+ if len(input.ContactType) == 0 {
+ return nil, httperrors.NewMissingParameterError("contact_type")
+ }
+ if !utils.IsInStringArray(input.ContactType, []string{api.EMAIL, api.MOBILE}) {
+ return nil, httperrors.NewInputParameterError("not support such contact type %q", input.ContactType)
+ }
+ verification, err := VerificationManager.Get(r.Id, input.ContactType)
+ if err != nil {
+ return nil, err
+ }
+ if verification.Token != input.Token {
+ return nil, httperrors.NewInputParameterError("wrong token")
+ }
+ _, err = db.Update(r, func() error {
+ switch input.ContactType {
+ case api.EMAIL:
+ r.VerifiedEmail = tristate.True
+ case api.MOBILE:
+ r.VerifiedMobile = tristate.True
+ default:
+ // no way
+ }
+ return nil
+ })
+ return nil, err
+}
+
+func (r *SReceiver) AllowPerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformEnableInput) bool {
+ return r.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, r, "enable")
+}
+
+func (r *SReceiver) PerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformEnableInput) (jsonutils.JSONObject, error) {
+ err := db.EnabledPerformEnable(r, ctx, userCred, true)
+ if err != nil {
+ return nil, errors.Wrap(err, "EnabledPerformEnable")
+ }
+ return nil, nil
+}
+
+func (r *SReceiver) AllowPerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput) bool {
+ return r.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, r, "disable")
+}
+
+func (r *SReceiver) PerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformDisableInput) (jsonutils.JSONObject, error) {
+ err := db.EnabledPerformEnable(r, ctx, userCred, false)
+ if err != nil {
+ return nil, errors.Wrap(err, "EnabledPerformEnable")
+ }
+ return nil, nil
+}
+
+// Implemente interface EventHandler
+func (rm *SReceiverManager) OnAdd(obj *jsonutils.JSONDict) {
+ // do nothing
+ return
+}
+
+func (rm *SReceiverManager) OnUpdate(oldObj, newObj *jsonutils.JSONDict) {
+ userId, _ := newObj.GetString("id")
+ receivers, err := rm.FetchByIDs(context.Background(), userId)
+ if err != nil {
+ log.Errorf("fail to FetchByIDs: %v", err)
+ return
+ }
+ receiver := &receivers[0]
+ uname, _ := newObj.GetString("name")
+ domainId, _ := newObj.GetString("domain_id")
+ if receiver.Name == uname && receiver.DomainId == domainId {
+ return
+ }
+ _, err = db.Update(receiver, func() error {
+ receiver.Name = uname
+ receiver.DomainId = domainId
+ return nil
+ })
+ if err != nil {
+ log.Errorf("fail to update uname of contact %q: %v", receiver.Id, err)
+ }
+}
+
+func (rm *SReceiverManager) OnDelete(obj *jsonutils.JSONDict) {
+ userId, _ := obj.GetString("id")
+ receivers, err := rm.FetchByIDs(context.Background(), userId)
+ if err != nil {
+ log.Errorf("fail to FetchByIDs: %v", err)
+ return
+ }
+ receiver := &receivers[0]
+ err = receiver.Delete(context.Background(), auth.GetAdminSession(context.Background(), "", "").GetToken())
+ if err != nil {
+ log.Errorf("fail to delete contact %q: %v", receiver.Id, err)
+ }
+}
+
+func (rm *SReceiverManager) StartWatchUserInKeystone() error {
+ adminSession := auth.GetAdminSession(context.Background(), "", "")
+ watchMan, err := informer.NewWatchManagerBySession(adminSession)
+ if err != nil {
+ return err
+ }
+ resMan := &modules.UsersV3
+ return watchMan.For(resMan).AddEventHandler(context.Background(), rm)
+}
+
+func (rm *SReceiverManager) FetchByIDs(ctx context.Context, ids ...string) ([]SReceiver, error) {
+ if len(ids) == 0 {
+ return nil, nil
+ }
+ var err error
+ q := rm.Query()
+ if len(ids) == 1 {
+ q = q.Equals("id", ids[0])
+ } else {
+ q = q.In("id", ids)
+ }
+ contacts := make([]SReceiver, 0, len(ids))
+ err = db.FetchModelObjects(rm, q, &contacts)
+ if err != nil {
+ return nil, err
+ }
+ return contacts, nil
+}
+
+func (rm *SReceiverManager) FetchByIdOrNames(ctx context.Context, idOrNames ...string) ([]SReceiver, error) {
+ if len(idOrNames) == 0 {
+ return nil, nil
+ }
+ var err error
+ q := rm.Query()
+ if len(idOrNames) == 1 {
+ q = q.Filter(sqlchemy.OR(
+ sqlchemy.Equals(q.Field("id"), idOrNames[0]),
+ sqlchemy.Equals(q.Field("name"), idOrNames[0]),
+ ))
+ } else {
+ q = q.Filter(sqlchemy.OR(
+ sqlchemy.In(q.Field("id"), idOrNames),
+ sqlchemy.In(q.Field("name"), idOrNames),
+ ))
+ }
+ receivers := make([]SReceiver, 0, len(idOrNames))
+ err = db.FetchModelObjects(rm, q, &receivers)
+ if err != nil {
+ return nil, err
+ }
+ return receivers, nil
+}
+
+func (r *SReceiver) GetExtraDetails(
+ ctx context.Context,
+ userCred mcclient.TokenCredential,
+ query jsonutils.JSONObject,
+ isList bool,
+) (api.ReceiverDetails, error) {
+ return api.ReceiverDetails{}, nil
+}
+
+func (r *SReceiver) SetContact(cType string, contact string) error {
+ if err := r.PullCache(false); err != nil {
+ return err
+ }
+ switch cType {
+ case api.EMAIL:
+ r.Email = contact
+ case api.MOBILE:
+ r.Mobile = contact
+ default:
+ if sc, ok := r.subContactCache[cType]; ok {
+ sc.Contact = contact
+ }
+ }
+ return nil
+}
+
+func (r *SReceiver) GetContact(cType string) (string, error) {
+ if err := r.PullCache(false); err != nil {
+ return "", err
+ }
+ switch {
+ case cType == api.EMAIL:
+ return r.Email, nil
+ case cType == api.MOBILE:
+ return r.Mobile, nil
+ case utils.IsInStringArray(cType, AllRobotContactTypes):
+ return r.Mobile, nil
+ default:
+ if sc, ok := r.subContactCache[cType]; ok {
+ return sc.Contact, nil
+ }
+ }
+ return "", nil
+}
diff --git a/pkg/notify/models/receiver_notification.go b/pkg/notify/models/receiver_notification.go
new file mode 100644
index 0000000000..0a9cc8a1c1
--- /dev/null
+++ b/pkg/notify/models/receiver_notification.go
@@ -0,0 +1,109 @@
+package models
+
+import (
+ "context"
+ "time"
+
+ api "yunion.io/x/onecloud/pkg/apis/notify"
+ "yunion.io/x/onecloud/pkg/cloudcommon/db"
+ "yunion.io/x/onecloud/pkg/mcclient"
+)
+
+var ReceiverNotificationManager *SReceiverNotificationManager
+
+func init() {
+ db.InitManager(func() {
+ ReceiverNotificationManager = &SReceiverNotificationManager{
+ SJointResourceBaseManager: db.NewJointResourceBaseManager(
+ SReceiverNotification{},
+ "receivernotification_tbl",
+ "receivernotification",
+ "receivernotifications",
+ NotificationManager,
+ ReceiverManager,
+ ),
+ }
+ ReceiverNotificationManager.SetVirtualObject(ReceiverNotificationManager)
+ })
+}
+
+type SReceiverNotificationManager struct {
+ db.SJointResourceBaseManager
+}
+
+// +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
+ Contact string `width:"128" nullable:"false"`
+ SendAt time.Time `nullable:"false"`
+ SendBy string `width:"128" nullable:"false"`
+ Status string `width:"36" charset:"ascii"`
+ FailedReason string `width:"1024"`
+}
+
+func (rnm *SReceiverNotificationManager) Create(ctx context.Context, userCred mcclient.TokenCredential, receiverID, notificationID string) (*SReceiverNotification, error) {
+ rn := &SReceiverNotification{
+ ReceiverID: receiverID,
+ NotificationID: notificationID,
+ Status: api.RECEIVER_NOTIFICATION_RECEIVED,
+ SendBy: userCred.GetUserId(),
+ }
+ return rn, rnm.TableSpec().Insert(ctx, rn)
+}
+
+func (rnm *SReceiverNotificationManager) GetMasterFieldName() string {
+ return "notification_id"
+}
+
+func (rnm *SReceiverNotificationManager) GetSlaveFieldName() string {
+ return "receiver_id"
+}
+
+func (rnm *SReceiverNotificationManager) CreateWithoutReceiver(ctx context.Context, userCred mcclient.TokenCredential, contact, notificationID string) (*SReceiverNotification, error) {
+ rn := &SReceiverNotification{
+ NotificationID: notificationID,
+ Contact: contact,
+ Status: api.RECEIVER_NOTIFICATION_RECEIVED,
+ SendBy: userCred.GetUserId(),
+ }
+ return rn, rnm.TableSpec().Insert(ctx, rn)
+}
+
+func (rn *SReceiverNotification) Receiver() (*SReceiver, error) {
+ q := ReceiverManager.Query().Equals("id", rn.ReceiverID)
+ var receiver SReceiver
+ err := q.First(&receiver)
+ if err != nil {
+ return nil, err
+ }
+ return &receiver, nil
+}
+
+func (rn *SReceiverNotification) BeforeSend(ctx context.Context, sendTime time.Time) error {
+ if sendTime.IsZero() {
+ sendTime = time.Now()
+ }
+ _, err := db.Update(rn, func() error {
+ rn.SendAt = sendTime
+ rn.Status = api.RECEIVER_NOTIFICATION_SENT
+ return nil
+ })
+ return err
+}
+
+func (rn *SReceiverNotification) AfterSend(ctx context.Context, success bool, reason string) error {
+ _, err := db.Update(rn, func() error {
+ if success {
+ rn.Status = api.RECEIVER_NOTIFICATION_OK
+ } else {
+ rn.Status = api.RECEIVER_NOTIFICATION_FAIL
+ rn.FailedReason = reason
+ }
+ return nil
+ })
+ return err
+}
diff --git a/pkg/notify/models/subcontact.go b/pkg/notify/models/subcontact.go
new file mode 100644
index 0000000000..1ba160dd2a
--- /dev/null
+++ b/pkg/notify/models/subcontact.go
@@ -0,0 +1,106 @@
+// Copyright 2019 Yunion
+//
+// 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 models
+
+import (
+ "yunion.io/x/pkg/tristate"
+
+ "yunion.io/x/onecloud/pkg/cloudcommon/db"
+)
+
+type SSubContactManager struct {
+ db.SStandaloneResourceBaseManager
+}
+
+// +onecloud:swagger-gen-ignore
+type SSubContact struct {
+ db.SStandaloneResourceBase
+
+ // id of receiver user
+ ReceiverID string `width:"128" nullable:"false" index:"true"`
+ Type string `width:"16" nullable:"false" index:"true"`
+ Contact string `width:"128" nullable:"false"`
+ ParentContactType string `width:"16" nullable:"false"`
+ Enabled tristate.TriState `nullable:"false" default:"false"`
+ Verified tristate.TriState `nullable:"false" default:"false"`
+}
+
+var SubContactManager *SSubContactManager
+
+var (
+ vTrue = true
+ vFalse = false
+
+ pTrue = &vTrue
+ pFalse = &vFalse
+)
+
+func init() {
+ SubContactManager = &SSubContactManager{
+ SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
+ SSubContact{},
+ "subcontacts_tbl",
+ "subcontact",
+ "subcontacts",
+ ),
+ }
+ SubContactManager.SetVirtualObject(SubContactManager)
+}
+
+func (scm *SSubContactManager) fetchMapByReceiverID(receiverID string) (map[string]*SSubContact, error) {
+ q := scm.Query().Equals("receiver_id", receiverID)
+ scontacts := make([]SSubContact, 0, 3)
+ err := db.FetchModelObjects(scm, q, &scontacts)
+ if err != nil {
+ return nil, err
+ }
+ ret := make(map[string]*SSubContact, len(scontacts))
+ for i := range scontacts {
+ ret[scontacts[i].Type] = &scontacts[i]
+ }
+ return ret, nil
+}
+
+func (sc *SSubContact) Enable() error {
+ return sc.Update(nil, pTrue, nil)
+}
+
+func (sc *SSubContact) Disable() error {
+ return sc.Update(nil, pFalse, nil)
+}
+
+func (sc *SSubContact) Verify() error {
+ return sc.Update(nil, nil, pTrue)
+}
+
+func (sc *SSubContact) Disverify() error {
+ return sc.Update(nil, nil, pFalse)
+}
+
+func (sc *SSubContact) Update(contact *string, enabled *bool, verified *bool) error {
+ _, err := db.Update(sc, func() error {
+ if contact != nil {
+ sc.Contact = *contact
+ }
+ if enabled != nil {
+ sc.Enabled = tristate.NewFromBool(*enabled)
+ }
+ if verified != nil {
+ sc.Verified = tristate.NewFromBool(*verified)
+ }
+ return nil
+ })
+ return err
+}
diff --git a/pkg/notify/models/mod_template.go b/pkg/notify/models/template.go
similarity index 57%
rename from pkg/notify/models/mod_template.go
rename to pkg/notify/models/template.go
index 1299a5fa65..0d76581cc7 100644
--- a/pkg/notify/models/mod_template.go
+++ b/pkg/notify/models/template.go
@@ -27,8 +27,10 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
+ "yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
+ api "yunion.io/x/onecloud/pkg/apis/notify"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
@@ -40,31 +42,29 @@ import (
)
type STemplateManager struct {
- SStandaloneResourceBaseManager
+ db.SStandaloneResourceBaseManager
}
var TemplateManager *STemplateManager
func init() {
TemplateManager = &STemplateManager{
- SStandaloneResourceBaseManager: NewStandaloneResourceBaseManager(
+ SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
STemplate{},
- "notify_t_template",
+ "template_tbl",
"notifytemplate",
"notifytemplates",
),
}
+ TemplateManager.SetVirtualObject(TemplateManager)
}
const (
- TEMPLATE_TYPE_TITLE = "title"
- TEMPLATE_TYPE_CONTENT = "content"
- TEMPLATE_TYPE_REMOTE = "remote"
- CONTACTTYPE_ALL = "all"
+ CONTACTTYPE_ALL = "all"
)
type STemplate struct {
- SStandaloneResourceBase
+ db.SStandaloneResourceBase
ContactType string `width:"16" nullable:"false" create:"required" update:"user" list:"user"`
Topic string `width:"20" nullable:"false" create:"required" update:"user" list:"user"`
@@ -72,21 +72,18 @@ type STemplate struct {
// title | content | remote
TemplateType string `width:"10" nullable:"false" create:"required" update:"user" list:"user"`
Content string `length:"text" nullable:"false" create:"required" get:"user" list:"user" update:"user"`
+ Example string `nullable:"false" created:"required" get:"user" list:"user" update:"user"`
}
const (
verifyUrlPath = "/email-verification/id/{0}/token/{1}?region=%s"
+ templatePath = "/opt/yunion/share/template"
)
func (tm *STemplateManager) GetEmailUrl() string {
- if len(options.Options.ApiServer) > 0 {
- return httputils.JoinPath(options.Options.ApiServer, fmt.Sprintf(verifyUrlPath, options.Options.Region))
- }
- return options.Options.VerifyEmailUrl
+ return httputils.JoinPath(options.Options.ApiServer, fmt.Sprintf(verifyUrlPath, options.Options.Region))
}
-var templatePath = "/opt/yunion/share/template"
-
func (tm *STemplateManager) defaultTemplate() ([]STemplate, error) {
templates := make([]STemplate, 0, 4)
@@ -143,6 +140,12 @@ func (tm *STemplateManager) GetCompanyInfo(ctx context.Context) (SCompanyInfo, e
return info, nil
}
+var (
+ ForceInitType = []string{
+ api.EMAIL,
+ }
+)
+
func (tm *STemplateManager) InitializeData() error {
templates, err := tm.defaultTemplate()
if err != nil {
@@ -151,12 +154,40 @@ func (tm *STemplateManager) InitializeData() error {
for _, template := range templates {
q := tm.Query().Equals("contact_type", template.ContactType).Equals("topic", template.Topic).Equals("template_type", template.TemplateType)
count, _ := q.CountWithError()
- if count > 0 {
+ if count > 0 && !utils.IsInStringArray(template.ContactType, ForceInitType) {
continue
}
- err := tm.TableSpec().InsertOrUpdate(context.TODO(), &template)
+ if count == 0 {
+ err := tm.TableSpec().Insert(context.TODO(), &template)
+ if err != nil {
+ return errors.Wrap(err, "sqlchemy.TableSpec.Insert")
+ }
+ continue
+ }
+ oldTemplates := make([]STemplate, 0, 1)
+ err := db.FetchModelObjects(tm, q, &oldTemplates)
if err != nil {
- return errors.Wrap(err, "sqlchemy.TableSpec.InsertOrUpdate")
+ return errors.Wrap(err, "db.FetchModelObjects")
+ }
+ // delete addtion
+ var (
+ ctx = context.Background()
+ userCred = auth.AdminCredential()
+ )
+ for i := 1; i < len(oldTemplates); i++ {
+ err := oldTemplates[i].Delete(ctx, userCred)
+ if err != nil {
+ return errors.Wrap(err, "STemplate.Delete")
+ }
+ }
+ // update
+ oldTemplate := &oldTemplates[0]
+ _, err = db.Update(oldTemplate, func() error {
+ oldTemplate.Content = template.Content
+ return nil
+ })
+ if err != nil {
+ return errors.Wrap(err, "db.Update")
}
}
return nil
@@ -182,19 +213,19 @@ func (tm *STemplateManager) NotifyFilter(contactType, topic, msg string) (params
for _, template := range templates {
var title, content string
switch template.TemplateType {
- case TEMPLATE_TYPE_TITLE:
+ case api.TEMPLATE_TYPE_TITLE:
title, err = template.Execute(msg)
if err != nil {
return
}
params.Title = title
- case TEMPLATE_TYPE_CONTENT:
+ case api.TEMPLATE_TYPE_CONTENT:
content, err = template.Execute(msg)
if err != nil {
return
}
params.Message = content
- case TEMPLATE_TYPE_REMOTE:
+ case api.TEMPLATE_TYPE_REMOTE:
params.RemoteTemplate = template.Content
params.Message = msg
default:
@@ -223,29 +254,69 @@ func (tm *STemplate) Execute(str string) (string, error) {
return buffer.String(), nil
}
-func (manager *STemplateManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential,
- ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) {
-
- ty, _ := data.GetString("template_type")
- if ty != TEMPLATE_TYPE_TITLE && ty != TEMPLATE_TYPE_CONTENT && ty != TEMPLATE_TYPE_REMOTE {
- return nil, httperrors.NewInputParameterError("no such support for tempalte type %s", ty)
+func (tm *STemplateManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.TemplateCreateInput) (api.TemplateCreateInput, error) {
+ if !utils.IsInStringArray(input.TemplateType, []string{
+ api.TEMPLATE_TYPE_CONTENT, api.TEMPLATE_TYPE_REMOTE, api.TEMPLATE_TYPE_TITLE,
+ }) {
+ return input, httperrors.NewInputParameterError("no such support for tempalte type %s", input.TemplateType)
}
- return data, nil
+ if input.TemplateType != api.TEMPLATE_TYPE_REMOTE {
+ if err := tm.validate(input.Content, input.Example); err != nil {
+ return input, httperrors.NewInputParameterError(err.Error())
+ }
+ }
+ if len(input.Name) == 0 {
+ input.Name = fmt.Sprintf("%s-%s-%s", input.ContactType, input.Topic, input.TemplateType)
+ }
+ return input, nil
}
-func (self *STemplateManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (*sqlchemy.SQuery, error) {
- queryDict := query.(*jsonutils.JSONDict)
- if queryDict.Contains("topic") {
- val, _ := queryDict.GetString("topic")
- q = q.Equals("topic", val)
+func (tm *STemplateManager) validate(template string, example string) error {
+ // check example availability
+ tem, err := ptem.New("tmp").Parse(template)
+ if err != nil {
+ return errors.Wrap(err, "invalid template")
}
- if queryDict.Contains("template_type") {
- val, _ := queryDict.GetString("template_type")
- q = q.Equals("template_type", val)
+ var buffer bytes.Buffer
+ tmpMap := make(map[string]interface{})
+ err = json.Unmarshal([]byte(example), &tmpMap)
+ if err != nil {
+ return errors.Wrap(err, "invalid example")
}
- if queryDict.Contains("contact_type") {
- val, _ := queryDict.GetString("contact_type")
- q = q.Equals("contact_type", val)
+ err = tem.Execute(&buffer, tmpMap)
+ if err != nil {
+ return errors.Wrap(err, "invalid example")
+ }
+ return nil
+}
+
+func (tm *STemplateManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.TemplateListInput) (*sqlchemy.SQuery, error) {
+ q, err := tm.SStandaloneResourceBaseManager.ListItemFilter(ctx, q, userCred, input.StandaloneResourceListInput)
+ if err != nil {
+ return nil, err
+ }
+ if len(input.Topic) > 0 {
+ q = q.Equals("topic", input.Topic)
+ }
+ if len(input.TemplateType) > 0 {
+ q = q.Equals("template_type", input.TemplateType)
+ }
+ if len(input.ContactType) > 0 {
+ q = q.Equals("contact_type", input.ContactType)
}
return q, nil
}
+
+func (t *STemplate) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.TemplateUpdateInput) (api.TemplateUpdateInput, error) {
+ if t.TemplateType == api.TEMPLATE_TYPE_REMOTE {
+ return input, nil
+ }
+ if err := TemplateManager.validate(input.Content, input.Example); err != nil {
+ return input, httperrors.NewInputParameterError(err.Error())
+ }
+ return input, nil
+}
+
+func (t *STemplate) GetExtraDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, isList bool) (api.TemplateDetails, error) {
+ return api.TemplateDetails{}, nil
+}
diff --git a/pkg/notify/models/verify.go b/pkg/notify/models/verify.go
new file mode 100644
index 0000000000..b171e62496
--- /dev/null
+++ b/pkg/notify/models/verify.go
@@ -0,0 +1,108 @@
+// Copyright 2019 Yunion
+//
+// 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 models
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "math/rand"
+ "time"
+
+ "yunion.io/x/pkg/errors"
+
+ "yunion.io/x/onecloud/pkg/cloudcommon/db"
+ "yunion.io/x/onecloud/pkg/notify/options"
+)
+
+type SVerificationManager struct {
+ db.SStandaloneResourceBaseManager
+}
+
+var VerificationManager *SVerificationManager
+
+func init() {
+ VerificationManager = &SVerificationManager{
+ SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
+ SVerification{},
+ "verification_tbl",
+ "verification",
+ "verifications",
+ ),
+ }
+ VerificationManager.SetVirtualObject(VerificationManager)
+}
+
+// +onecloud:swagger-gen-ignore
+type SVerification struct {
+ db.SStandaloneResourceBase
+
+ ReceiverId string `width:"128" nullable:"false"`
+ ContactType string `width:"16" nullable:"false"`
+ Token string `width:"200" nullable:"false"`
+}
+
+var ErrVerifyFrequently = errors.Error("Send validation messages too frequently")
+
+func (vm *SVerificationManager) generateVerifyToken() string {
+ rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
+ token := fmt.Sprintf("%06v", rnd.Int31n(1000000))
+ return token
+}
+
+func (vm *SVerificationManager) Create(ctx context.Context, receiverId, contactType string) (*SVerification, error) {
+ // try to reuse
+ ret, err := vm.Get(receiverId, contactType)
+ if err != nil && errors.Cause(err) != sql.ErrNoRows {
+ return nil, err
+ }
+ if ret == nil {
+ ret = &SVerification{
+ ReceiverId: receiverId,
+ ContactType: contactType,
+ Token: vm.generateVerifyToken(),
+ }
+ err := vm.TableSpec().Insert(ctx, ret)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ now := time.Now()
+ if now.Before(ret.CreatedAt.Add(time.Duration(options.Options.VerifyExpireInterval) * time.Minute)) {
+ return nil, ErrVerifyFrequently
+ }
+ _, err := db.Update(ret, func() error {
+ ret.Token = vm.generateVerifyToken()
+ ret.CreatedAt = now
+ ret.UpdatedAt = now
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ }
+ return ret, nil
+}
+
+func (vm *SVerificationManager) Get(receiverId, contactType string) (*SVerification, error) {
+ q := vm.Query().Equals("receiver_id", receiverId).Equals("contact_type", contactType)
+ var verification SVerification
+ err := q.First(&verification)
+ if err != nil {
+ return nil, err
+ }
+ verification.SetModelManager(vm, &verification)
+ return &verification, nil
+}
diff --git a/pkg/notify/models/worker.go b/pkg/notify/models/worker.go
deleted file mode 100644
index 3c90fbe1b9..0000000000
--- a/pkg/notify/models/worker.go
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 models
-
-import (
- "context"
- "fmt"
- "strings"
- "time"
-
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
-
- "yunion.io/x/onecloud/pkg/appsrv"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/notify/utils"
-)
-
-var workMan *appsrv.SWorkerManager
-
-func init() {
- workMan = appsrv.NewWorkerManager("NotifyWokerManager", 16, 512, false)
-}
-
-func Send(notifications []*SNotification, userCred mcclient.TokenCredential, contacts []string) {
-
- for i := range notifications {
- notification, contact := notifications[i], contacts[i]
- workMan.Run(func() {
- sendone(context.Background(), userCred, notification, contact)
- }, nil, nil)
- }
-}
-
-func sendone(ctx context.Context, userCred mcclient.TokenCredential, notification *SNotification, contact string) {
- err := notification.SetSentAndTime(userCred)
- if err != nil {
- log.Errorf("Change notification's status failed.")
- return
- }
- err = NotifyService.Send(ctx, notification.ContactType, contact, notification.Topic, notification.Msg,
- notification.Priority)
- if err != nil {
- log.Errorf("Send notification failed: %s.", err.Error())
- notification.SetStatus(userCred, NOTIFY_FAIL, err.Error())
- } else {
- log.Debugf("send notification successfully")
- notification.SetStatus(userCred, NOTIFY_OK, "")
- }
-}
-
-func RestartService(config map[string]string, serviceName string) {
- workMan.Run(func() {
- NotifyService.RestartService(context.Background(), config, serviceName)
- }, nil, nil)
-}
-
-func SendVerifyMessage(ctx context.Context, userCred mcclient.TokenCredential, verify *SVerify,
- contact *SContact) error {
- var (
- err error
- msg string
- )
- info, err := TemplateManager.GetCompanyInfo(ctx)
- if err != nil {
- log.Errorf("unable to try to get company info: %s", err.Error())
- }
- processId, token := verify.ID, verify.Token
- if contact.ContactType == "email" {
- emailUrl := strings.Replace(TemplateManager.GetEmailUrl(), "{0}", processId, 1)
- emailUrl = strings.Replace(emailUrl, "{1}", token, 1)
-
- // get uName
- uName, err := utils.GetUsernameByID(ctx, contact.UID)
- if err != nil || len(uName) == 0 {
- uName = "用户"
- }
- data := struct {
- Name string
- Link string
- SCompanyInfo
- }{
- Name: uName,
- Link: emailUrl,
- SCompanyInfo: info,
- }
- msg = jsonutils.Marshal(data).String()
- } else if contact.ContactType == "mobile" {
- msg = fmt.Sprintf(`{"code": "%s"}`, token)
- } else {
- // todo
- return nil
- }
-
- err = NotifyService.Send(ctx, contact.ContactType, contact.Contact, "verify", msg, "")
- if err != nil {
- verify.SetStatus(userCred, VERIFICATION_SENT_FAIL, "")
- // set contact's status as "init"
- contact.SetStatus(userCred, CONTACT_INIT, "send verify message failed")
- log.Errorf("Send verify message failed: %s.", err.Error())
- return errors.Wrap(err, "Send Verify Message Failed")
- }
- verify.SetStatus(userCred, VERIFICATION_SENT, "")
- return nil
-}
-
-func PullContact(uid string, contactTypes []string) {
- for i := range contactTypes {
- ct := contactTypes[i]
- workMan.Run(func() {
- pullContact(context.Background(), uid, ct)
- }, nil, nil)
- }
-}
-
-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, subContact *SContact
- for i := range contacts {
- if contacts[i].ContactType == MOBILE {
- mobileContact = &contacts[i]
- } else {
- subContact = &contacts[i]
- }
- }
- if mobileContact == nil {
- return
- }
-
- userid, err := NotifyService.ContactByMobile(ctx, mobileContact.Contact, contactType)
- if err != nil {
- log.Errorf("fetch %s contact by mobile failed: %s", contactType, err.Error())
- }
- if subContact != nil {
- subContact.SetModelManager(ContactManager, subContact)
- origin := subContact.Contact
- _, err := db.Update(subContact, func() error {
- subContact.Contact = userid
- subContact.VerifiedAt = time.Now()
- if subContact.Status != CONTACT_VERIFIED {
- subContact.Status = CONTACT_VERIFIED
- }
- return nil
- })
- if err != nil {
- log.Errorf("update %s contact userid %s => %s failed", contactType, origin, userid)
- }
- return
- }
-
- contact := SContact{
- UID: uid,
- ContactType: contactType,
- Contact: userid,
- Enabled: "1",
- VerifiedAt: time.Now(),
- }
- contact.Status = CONTACT_VERIFIED
-
- err = ContactManager.TableSpec().Insert(ctx, &contact)
- if err != nil {
- log.Errorf("create new %s contact failed", contactType)
- }
-}
diff --git a/pkg/notify/models/base.go b/pkg/notify/oldmodels/base.go
similarity index 99%
rename from pkg/notify/models/base.go
rename to pkg/notify/oldmodels/base.go
index f94879ecc8..d2b72685ee 100644
--- a/pkg/notify/models/base.go
+++ b/pkg/notify/oldmodels/base.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package models
+package oldmodels
import (
"context"
diff --git a/pkg/notify/interface/doc.go b/pkg/notify/oldmodels/doc.go
similarity index 88%
rename from pkg/notify/interface/doc.go
rename to pkg/notify/oldmodels/doc.go
index 203113f4d6..1953161f39 100644
--- a/pkg/notify/interface/doc.go
+++ b/pkg/notify/oldmodels/doc.go
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package _interface // import "yunion.io/x/onecloud/pkg/notify/interface"
+package oldmodels // import "yunion.io/x/onecloud/pkg/notify/oldmodels"
diff --git a/pkg/notify/oldmodels/mod_config.go b/pkg/notify/oldmodels/mod_config.go
new file mode 100644
index 0000000000..a70b06fcba
--- /dev/null
+++ b/pkg/notify/oldmodels/mod_config.go
@@ -0,0 +1,44 @@
+// Copyright 2019 Yunion
+//
+// 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 oldmodels
+
+type SConfigManager struct {
+ SStatusStandaloneResourceBaseManager
+}
+
+var ConfigManager *SConfigManager
+
+func init() {
+ ConfigManager = &SConfigManager{
+ SStatusStandaloneResourceBaseManager: NewStatusStandaloneResourceBaseManager(
+ SConfig{},
+ "notify_t_config",
+ "oldconfig",
+ "oldconfigs",
+ ),
+ }
+ ConfigManager.SetVirtualObject(ConfigManager)
+}
+
+// SConfig is a table which storage (k,v) and its type.
+// The three important concepts are key, value and type.
+// Key and type uniquely identify a value.
+type SConfig struct {
+ SStatusStandaloneResourceBase
+
+ Type string `width:"15" nullable:"false" create:"required" list:"user"`
+ KeyText string `width:"30" nullable:"false" create:"required" list:"user"`
+ ValueText string `width:"256" nullable:"false" create:"required" list:"user"`
+}
diff --git a/pkg/notify/oldmodels/mod_contact.go b/pkg/notify/oldmodels/mod_contact.go
new file mode 100644
index 0000000000..f33ec8b615
--- /dev/null
+++ b/pkg/notify/oldmodels/mod_contact.go
@@ -0,0 +1,53 @@
+// Copyright 2019 Yunion
+//
+// 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 oldmodels
+
+import (
+ "time"
+)
+
+type SContactManager struct {
+ SStatusStandaloneResourceBaseManager
+}
+
+var ContactManager *SContactManager
+
+func init() {
+ ContactManager = &SContactManager{
+ SStatusStandaloneResourceBaseManager: NewStatusStandaloneResourceBaseManager(
+ SContact{},
+ "notify_t_contacts",
+ "contact",
+ "contacts",
+ ),
+ }
+ ContactManager.SetVirtualObject(ContactManager)
+}
+
+const (
+ CONTACT_INIT = "init" // Contact's status is init which means no verifying
+ CONTACT_VERIFYING = "verifying" // Contact's status is verifying
+ CONTACT_VERIFIED = "verified" // Contact's status is verified
+)
+
+type SContact struct {
+ SStatusStandaloneResourceBase
+
+ UID string `width:"128" nullable:"false" create:"required" update:"user" list:"user" get:"user"`
+ ContactType string `width:"16" nullable:"false" create:"required" update:"user"`
+ Contact string `width:"64" nullable:"false" create:"required" update:"user"`
+ Enabled string `width:"5" nullable:"false" default:"1" create:"optional" update:"user"`
+ VerifiedAt time.Time `update:"user"`
+}
diff --git a/pkg/notify/oldmodels/mod_notification.go b/pkg/notify/oldmodels/mod_notification.go
new file mode 100644
index 0000000000..d2eacf82a3
--- /dev/null
+++ b/pkg/notify/oldmodels/mod_notification.go
@@ -0,0 +1,52 @@
+// Copyright 2019 Yunion
+//
+// 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 oldmodels
+
+import (
+ "time"
+)
+
+type SNotificationManager struct {
+ SStatusStandaloneResourceBaseManager
+}
+
+var NotificationManager *SNotificationManager
+
+func init() {
+ NotificationManager = &SNotificationManager{
+ SStatusStandaloneResourceBaseManager: NewStatusStandaloneResourceBaseManager(
+ SNotification{},
+ "notify_t_notification",
+ "oldnotification",
+ "oldnotifications",
+ ),
+ }
+ NotificationManager.SetVirtualObject(NotificationManager)
+}
+
+type SNotification struct {
+ SStatusStandaloneResourceBase
+
+ UID string `width:"128" nullable:"false" create:"required"`
+ ContactType string `width:"16" nullable:"false" create:"required" list:"user" index:"true"`
+ Topic string `width:"128" nullable:"true" create:"optional" list:"user"`
+ Priority string `width:"16" nullable:"true" create:"optional" list:"user"`
+ Msg string `create:"required"`
+ ReceivedAt time.Time `nullable:"true" list:"user" create:"optional"`
+ SendAt time.Time `nullable:"false"`
+ SendBy string `width:"128" nullable:"false"`
+ // ClusterID identify message with same topic, msg, priority
+ ClusterID string `width:"128" charset:"ascii" primary:"true" create:"optional" list:"user" get:"user"`
+}
diff --git a/pkg/notify/oldmodels/mod_template.go b/pkg/notify/oldmodels/mod_template.go
new file mode 100644
index 0000000000..c25688f290
--- /dev/null
+++ b/pkg/notify/oldmodels/mod_template.go
@@ -0,0 +1,51 @@
+// Copyright 2019 Yunion
+//
+// 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 oldmodels
+
+type STemplateManager struct {
+ SStandaloneResourceBaseManager
+}
+
+var TemplateManager *STemplateManager
+
+func init() {
+ TemplateManager = &STemplateManager{
+ SStandaloneResourceBaseManager: NewStandaloneResourceBaseManager(
+ STemplate{},
+ "notify_t_template",
+ "oldtemplate",
+ "oldtemplates",
+ ),
+ }
+ TemplateManager.SetVirtualObject(TemplateManager)
+}
+
+const (
+ TEMPLATE_TYPE_TITLE = "title"
+ TEMPLATE_TYPE_CONTENT = "content"
+ TEMPLATE_TYPE_REMOTE = "remote"
+ CONTACTTYPE_ALL = "all"
+)
+
+type STemplate struct {
+ SStandaloneResourceBase
+
+ ContactType string `width:"16" nullable:"false" create:"required" update:"user" list:"user"`
+ Topic string `width:"20" nullable:"false" create:"required" update:"user" list:"user"`
+
+ // title | content | remote
+ TemplateType string `width:"10" nullable:"false" create:"required" update:"user" list:"user"`
+ Content string `length:"text" nullable:"false" create:"required" get:"user" list:"user" update:"user"`
+}
diff --git a/pkg/notify/models/standalone.go b/pkg/notify/oldmodels/standalone.go
similarity index 99%
rename from pkg/notify/models/standalone.go
rename to pkg/notify/oldmodels/standalone.go
index 1c20a01bd0..437d81a66f 100644
--- a/pkg/notify/models/standalone.go
+++ b/pkg/notify/oldmodels/standalone.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package models
+package oldmodels
import (
"database/sql"
diff --git a/pkg/notify/models/statusstandalone.go b/pkg/notify/oldmodels/statusstandalone.go
similarity index 99%
rename from pkg/notify/models/statusstandalone.go
rename to pkg/notify/oldmodels/statusstandalone.go
index 64908a30e9..d6851ad10b 100644
--- a/pkg/notify/models/statusstandalone.go
+++ b/pkg/notify/oldmodels/statusstandalone.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package models
+package oldmodels
import (
"context"
diff --git a/pkg/notify/cache/usercache.go b/pkg/notify/oldmodels/usercache.go
similarity index 97%
rename from pkg/notify/cache/usercache.go
rename to pkg/notify/oldmodels/usercache.go
index 160de8a664..33436ed60f 100644
--- a/pkg/notify/cache/usercache.go
+++ b/pkg/notify/oldmodels/usercache.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cache
+package oldmodels
import (
"context"
@@ -42,7 +42,7 @@ var UserCacheManager *SUserCacheManager
func init() {
dbUserCacheManager := db.SUserCacheManager{
SKeystoneCacheObjectManager: db.NewKeystoneCacheObjectManager(
- db.SUser{}, "users_cache_tbl", "user", "users"),
+ db.SUser{}, "users_cache_tbl", "olduser", "oldusers"),
}
UserCacheManager = &SUserCacheManager{
dbUserCacheManager,
diff --git a/pkg/notify/options/options.go b/pkg/notify/options/options.go
index c62487f7fa..26113bddba 100644
--- a/pkg/notify/options/options.go
+++ b/pkg/notify/options/options.go
@@ -22,17 +22,16 @@ type NotifyOption struct {
common_options.CommonOptions
common_options.DBOptions
- DingtalkEnabled bool `help:"Enable dingtalk"`
- SocketFileDir string `help:"Socket file directory" default:"/etc/yunion/notify"`
- UpdateInterval int `help:"Update send services interval(unit:min)" default:"30"`
- // VerifyEmailUrlPath string `help:"url of verify email" json:"verify_email_url_path"`
+ SocketFileDir string `help:"Socket file directory" default:"/etc/yunion/socket"`
+ UpdateInterval int `help:"Update send services interval(unit:min)" default:"30"`
- // Deprecated
- VerifyEmailUrl string `help:"url of verify email" json:"verify_email_url"`
-
- ReSendScope int `help:"Resend all messages that have not been sent successfully within ReSendScope seconds" default:"30"`
+ ReSendScope int `help:"Resend all messages that have not been sent successfully within ReSendScope seconds" default:"60"`
+ MaxSendTimes int `help:"Resend all messages whose sendTimes less than MaxSendTimes" default:"2"`
InitNotificationScope int `help:"initialize data of notification with in InitNotificationScope hours" default:"100"`
+ MaxSyncNotification int `help:"The max number of notification sync from old data source" default:"1000"`
+
+ VerifyExpireInterval int `help:"expire interval of verify message; minutes" default:"5"`
}
var Options NotifyOption
diff --git a/pkg/notify/rpc/apis/send_client.go b/pkg/notify/rpc/apis/send_client.go
index 6391bb1a2d..64ad790004 100644
--- a/pkg/notify/rpc/apis/send_client.go
+++ b/pkg/notify/rpc/apis/send_client.go
@@ -58,3 +58,9 @@ func (c *SendNotificationClient) UseridByMobile(ctx context.Context, in *UseridB
defer cancel()
return c.sendAgentClient.UseridByMobile(ctx, in, opts...)
}
+
+func (c *SendNotificationClient) BatchSend(ctx context.Context, in *BatchSendParams, opts ...grpc.CallOption) (*BatchSendReply, error) {
+ ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
+ defer cancel()
+ return c.sendAgentClient.BatchSend(ctx, in, opts...)
+}
diff --git a/pkg/notify/rpc/apis/send_server.pb.go b/pkg/notify/rpc/apis/send_server.pb.go
index 40ddf6bf3c..c3106b46b3 100644
--- a/pkg/notify/rpc/apis/send_server.pb.go
+++ b/pkg/notify/rpc/apis/send_server.pb.go
@@ -1,17 +1,3 @@
-// Copyright 2019 Yunion
-//
-// 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.
-
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: send_server.proto
@@ -313,6 +299,163 @@ func (m *ValidateConfigReply) GetMsg() string {
return ""
}
+type BatchSendParams struct {
+ Contacts []string `protobuf:"bytes,1,rep,name=Contacts,proto3" json:"Contacts,omitempty"`
+ Title string `protobuf:"bytes,2,opt,name=Title,proto3" json:"Title,omitempty"`
+ Message string `protobuf:"bytes,3,opt,name=Message,proto3" json:"Message,omitempty"`
+ Priority string `protobuf:"bytes,4,opt,name=Priority,proto3" json:"Priority,omitempty"`
+ RemoteTemplate string `protobuf:"bytes,5,opt,name=RemoteTemplate,proto3" json:"RemoteTemplate,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *BatchSendParams) Reset() { *m = BatchSendParams{} }
+func (m *BatchSendParams) String() string { return proto.CompactTextString(m) }
+func (*BatchSendParams) ProtoMessage() {}
+func (*BatchSendParams) Descriptor() ([]byte, []int) {
+ return fileDescriptor_63fdd68f7eb311f9, []int{6}
+}
+
+func (m *BatchSendParams) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_BatchSendParams.Unmarshal(m, b)
+}
+func (m *BatchSendParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_BatchSendParams.Marshal(b, m, deterministic)
+}
+func (m *BatchSendParams) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_BatchSendParams.Merge(m, src)
+}
+func (m *BatchSendParams) XXX_Size() int {
+ return xxx_messageInfo_BatchSendParams.Size(m)
+}
+func (m *BatchSendParams) XXX_DiscardUnknown() {
+ xxx_messageInfo_BatchSendParams.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BatchSendParams proto.InternalMessageInfo
+
+func (m *BatchSendParams) GetContacts() []string {
+ if m != nil {
+ return m.Contacts
+ }
+ return nil
+}
+
+func (m *BatchSendParams) GetTitle() string {
+ if m != nil {
+ return m.Title
+ }
+ return ""
+}
+
+func (m *BatchSendParams) GetMessage() string {
+ if m != nil {
+ return m.Message
+ }
+ return ""
+}
+
+func (m *BatchSendParams) GetPriority() string {
+ if m != nil {
+ return m.Priority
+ }
+ return ""
+}
+
+func (m *BatchSendParams) GetRemoteTemplate() string {
+ if m != nil {
+ return m.RemoteTemplate
+ }
+ return ""
+}
+
+type FailedRecord struct {
+ Contact string `protobuf:"bytes,1,opt,name=Contact,proto3" json:"Contact,omitempty"`
+ Reason string `protobuf:"bytes,2,opt,name=Reason,proto3" json:"Reason,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *FailedRecord) Reset() { *m = FailedRecord{} }
+func (m *FailedRecord) String() string { return proto.CompactTextString(m) }
+func (*FailedRecord) ProtoMessage() {}
+func (*FailedRecord) Descriptor() ([]byte, []int) {
+ return fileDescriptor_63fdd68f7eb311f9, []int{7}
+}
+
+func (m *FailedRecord) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_FailedRecord.Unmarshal(m, b)
+}
+func (m *FailedRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_FailedRecord.Marshal(b, m, deterministic)
+}
+func (m *FailedRecord) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_FailedRecord.Merge(m, src)
+}
+func (m *FailedRecord) XXX_Size() int {
+ return xxx_messageInfo_FailedRecord.Size(m)
+}
+func (m *FailedRecord) XXX_DiscardUnknown() {
+ xxx_messageInfo_FailedRecord.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_FailedRecord proto.InternalMessageInfo
+
+func (m *FailedRecord) GetContact() string {
+ if m != nil {
+ return m.Contact
+ }
+ return ""
+}
+
+func (m *FailedRecord) GetReason() string {
+ if m != nil {
+ return m.Reason
+ }
+ return ""
+}
+
+type BatchSendReply struct {
+ FailedRecords []*FailedRecord `protobuf:"bytes,1,rep,name=FailedRecords,proto3" json:"FailedRecords,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *BatchSendReply) Reset() { *m = BatchSendReply{} }
+func (m *BatchSendReply) String() string { return proto.CompactTextString(m) }
+func (*BatchSendReply) ProtoMessage() {}
+func (*BatchSendReply) Descriptor() ([]byte, []int) {
+ return fileDescriptor_63fdd68f7eb311f9, []int{8}
+}
+
+func (m *BatchSendReply) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_BatchSendReply.Unmarshal(m, b)
+}
+func (m *BatchSendReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_BatchSendReply.Marshal(b, m, deterministic)
+}
+func (m *BatchSendReply) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_BatchSendReply.Merge(m, src)
+}
+func (m *BatchSendReply) XXX_Size() int {
+ return xxx_messageInfo_BatchSendReply.Size(m)
+}
+func (m *BatchSendReply) XXX_DiscardUnknown() {
+ xxx_messageInfo_BatchSendReply.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BatchSendReply proto.InternalMessageInfo
+
+func (m *BatchSendReply) GetFailedRecords() []*FailedRecord {
+ if m != nil {
+ return m.FailedRecords
+ }
+ return nil
+}
+
func init() {
proto.RegisterType((*SendParams)(nil), "apis.SendParams")
proto.RegisterType((*UpdateConfigParams)(nil), "apis.UpdateConfigParams")
@@ -321,37 +464,47 @@ func init() {
proto.RegisterType((*Empty)(nil), "apis.Empty")
proto.RegisterType((*UseridByMobileReply)(nil), "apis.UseridByMobileReply")
proto.RegisterType((*ValidateConfigReply)(nil), "apis.ValidateConfigReply")
+ proto.RegisterType((*BatchSendParams)(nil), "apis.BatchSendParams")
+ proto.RegisterType((*FailedRecord)(nil), "apis.FailedRecord")
+ proto.RegisterType((*BatchSendReply)(nil), "apis.BatchSendReply")
}
func init() { proto.RegisterFile("send_server.proto", fileDescriptor_63fdd68f7eb311f9) }
var fileDescriptor_63fdd68f7eb311f9 = []byte{
- // 398 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0xcf, 0x6a, 0xdb, 0x40,
- 0x10, 0xc6, 0x51, 0xfc, 0x47, 0xc9, 0x24, 0x98, 0x74, 0x13, 0xca, 0x56, 0xa7, 0x20, 0x70, 0xf1,
- 0xa5, 0x3a, 0xb8, 0x14, 0x8a, 0x2f, 0xc5, 0x35, 0xa6, 0x27, 0x83, 0x51, 0xed, 0x5e, 0xcb, 0xda,
- 0x9a, 0x8a, 0xa5, 0x92, 0x56, 0xec, 0xae, 0x0d, 0x7a, 0x8c, 0xbe, 0x49, 0x5f, 0xaf, 0xb7, 0xb2,
- 0x7f, 0x54, 0x5b, 0xad, 0x9b, 0x9b, 0x7e, 0xdf, 0xcc, 0x37, 0xcc, 0x7c, 0x5a, 0x78, 0xa1, 0xb0,
- 0xca, 0xbe, 0x2a, 0x94, 0x47, 0x94, 0x49, 0x2d, 0x85, 0x16, 0xa4, 0xcf, 0x6a, 0xae, 0xe2, 0x9f,
- 0x01, 0xc0, 0x67, 0xac, 0xb2, 0x35, 0x93, 0xac, 0x54, 0x84, 0x42, 0xb8, 0x10, 0x95, 0x66, 0x7b,
- 0x4d, 0x83, 0xa7, 0x60, 0x72, 0x93, 0xb6, 0x48, 0x1e, 0x61, 0xb0, 0x11, 0x35, 0xdf, 0xd3, 0x2b,
- 0xab, 0x3b, 0xb0, 0x2a, 0xd7, 0x05, 0xd2, 0x9e, 0x57, 0x0d, 0x98, 0x29, 0x2b, 0x54, 0x8a, 0xe5,
- 0x48, 0xfb, 0x6e, 0x8a, 0x47, 0x12, 0xc1, 0xf5, 0x5a, 0x72, 0x21, 0xb9, 0x6e, 0xe8, 0xc0, 0x96,
- 0xfe, 0x30, 0x79, 0x0d, 0xa3, 0x14, 0x4b, 0xa1, 0x71, 0x83, 0x65, 0x5d, 0x30, 0x8d, 0x74, 0x68,
- 0x3b, 0xfe, 0x52, 0xe3, 0x1f, 0x01, 0x90, 0x6d, 0x9d, 0x31, 0x8d, 0x0b, 0x51, 0x7d, 0xe3, 0xb9,
- 0x5f, 0xfd, 0x03, 0x84, 0x7b, 0xcb, 0x8a, 0x06, 0x4f, 0xbd, 0xc9, 0xed, 0x74, 0x9c, 0x98, 0x0b,
- 0x93, 0x7f, 0x5b, 0x13, 0x07, 0x6a, 0x59, 0x69, 0xd9, 0xa4, 0xad, 0x2b, 0x9a, 0xc1, 0xdd, 0x79,
- 0x81, 0xdc, 0x43, 0xef, 0x3b, 0x36, 0x3e, 0x07, 0xf3, 0x69, 0xae, 0x3d, 0xb2, 0xe2, 0x80, 0x6d,
- 0x06, 0x16, 0x66, 0x57, 0xef, 0x83, 0x38, 0x81, 0xc7, 0xad, 0x42, 0xc9, 0xb3, 0x8f, 0xcd, 0x4a,
- 0xec, 0x78, 0x81, 0x7e, 0xa9, 0x97, 0x30, 0x2c, 0x2d, 0xfb, 0x31, 0x9e, 0xe2, 0x10, 0x06, 0xcb,
- 0xb2, 0xd6, 0x4d, 0xfc, 0x06, 0x1e, 0xba, 0xc6, 0x14, 0xeb, 0xa2, 0x31, 0xbe, 0x83, 0x95, 0x5b,
- 0x9f, 0xa3, 0x78, 0x0e, 0x0f, 0x5f, 0x58, 0xc1, 0x4f, 0x17, 0xb9, 0x76, 0x0a, 0x21, 0x57, 0xb6,
- 0x60, 0xfb, 0xaf, 0xd3, 0x16, 0xcd, 0x11, 0xa5, 0xca, 0xfd, 0xc2, 0xe6, 0x73, 0xfa, 0x2b, 0x80,
- 0x1b, 0xf3, 0xc7, 0xe7, 0x39, 0x56, 0x9a, 0x8c, 0xa1, 0x6f, 0x80, 0xdc, 0xbb, 0xb0, 0x4e, 0x4f,
- 0x21, 0xba, 0x75, 0x8a, 0x5d, 0x93, 0xbc, 0x83, 0xbb, 0xf3, 0x1c, 0x09, 0xfd, 0x5f, 0xb6, 0x5d,
- 0xdb, 0x12, 0x46, 0xdd, 0x75, 0x9f, 0x31, 0xbe, 0x72, 0x95, 0x4b, 0xe7, 0x7d, 0x82, 0x51, 0x37,
- 0x24, 0x12, 0xf9, 0x31, 0x17, 0x32, 0x6f, 0x07, 0x5d, 0x88, 0x75, 0x37, 0xb4, 0x4f, 0xff, 0xed,
- 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa6, 0x8d, 0x12, 0x77, 0x0f, 0x03, 0x00, 0x00,
+ // 497 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xdd, 0x6a, 0xd4, 0x40,
+ 0x14, 0xc7, 0xc9, 0x7e, 0xef, 0xe9, 0xba, 0xd6, 0xe9, 0x5a, 0xc6, 0x5c, 0x95, 0x40, 0xa5, 0x37,
+ 0xe6, 0xa2, 0x22, 0x2c, 0xbd, 0xd1, 0xb6, 0xac, 0x82, 0x50, 0x28, 0xb1, 0xf5, 0x56, 0xa6, 0xc9,
+ 0x71, 0x1d, 0x4c, 0x32, 0x21, 0x33, 0x2d, 0xe4, 0x31, 0x7c, 0x04, 0xdf, 0x40, 0xf0, 0x05, 0x65,
+ 0x3e, 0xb2, 0x9b, 0xac, 0xbb, 0xed, 0x5d, 0x7e, 0xe7, 0x8b, 0x39, 0xe7, 0xff, 0x27, 0xf0, 0x42,
+ 0x62, 0x9e, 0x7c, 0x93, 0x58, 0x3e, 0x60, 0x19, 0x16, 0xa5, 0x50, 0x82, 0xf4, 0x58, 0xc1, 0x65,
+ 0xf0, 0xc7, 0x03, 0xf8, 0x82, 0x79, 0x72, 0xcd, 0x4a, 0x96, 0x49, 0x42, 0x61, 0x78, 0x29, 0x72,
+ 0xc5, 0x62, 0x45, 0xbd, 0x23, 0xef, 0x64, 0x1c, 0xd5, 0x48, 0x66, 0xd0, 0xbf, 0x11, 0x05, 0x8f,
+ 0x69, 0xc7, 0xc4, 0x2d, 0x98, 0x28, 0x57, 0x29, 0xd2, 0xae, 0x8b, 0x6a, 0xd0, 0x53, 0xae, 0x50,
+ 0x4a, 0xb6, 0x44, 0xda, 0xb3, 0x53, 0x1c, 0x12, 0x1f, 0x46, 0xd7, 0x25, 0x17, 0x25, 0x57, 0x15,
+ 0xed, 0x9b, 0xd4, 0x8a, 0xc9, 0x6b, 0x98, 0x46, 0x98, 0x09, 0x85, 0x37, 0x98, 0x15, 0x29, 0x53,
+ 0x48, 0x07, 0xa6, 0x62, 0x23, 0x1a, 0xfc, 0xf2, 0x80, 0xdc, 0x16, 0x09, 0x53, 0x78, 0x29, 0xf2,
+ 0xef, 0x7c, 0xe9, 0x9e, 0xfe, 0x1e, 0x86, 0xb1, 0x61, 0x49, 0xbd, 0xa3, 0xee, 0xc9, 0xde, 0xe9,
+ 0x71, 0xa8, 0x37, 0x0c, 0xff, 0x2f, 0x0d, 0x2d, 0xc8, 0x45, 0xae, 0xca, 0x2a, 0xaa, 0xbb, 0xfc,
+ 0x33, 0x98, 0x34, 0x13, 0x64, 0x1f, 0xba, 0x3f, 0xb1, 0x72, 0x77, 0xd0, 0x9f, 0x7a, 0xdb, 0x07,
+ 0x96, 0xde, 0x63, 0x7d, 0x03, 0x03, 0x67, 0x9d, 0xb9, 0x17, 0x84, 0x30, 0xbb, 0x95, 0x58, 0xf2,
+ 0xe4, 0xa2, 0xba, 0x12, 0x77, 0x3c, 0x45, 0xf7, 0xa8, 0x43, 0x18, 0x64, 0x86, 0xdd, 0x18, 0x47,
+ 0xc1, 0x10, 0xfa, 0x8b, 0xac, 0x50, 0x55, 0xf0, 0x06, 0x0e, 0xda, 0x8d, 0x11, 0x16, 0x69, 0xa5,
+ 0xfb, 0xee, 0x4d, 0xb8, 0xee, 0xb3, 0x14, 0x9c, 0xc3, 0xc1, 0x57, 0x96, 0xf2, 0xf5, 0x46, 0xb6,
+ 0x9c, 0xc2, 0x90, 0x4b, 0x93, 0x30, 0xf5, 0xa3, 0xa8, 0x46, 0xbd, 0x44, 0x26, 0x97, 0xee, 0xc1,
+ 0xfa, 0x33, 0xf8, 0xed, 0xc1, 0xf3, 0x0b, 0xa6, 0xe2, 0x1f, 0x0d, 0xd9, 0x7d, 0x18, 0x39, 0x9d,
+ 0xed, 0xf1, 0xc6, 0xd1, 0x8a, 0xd7, 0x12, 0x77, 0x76, 0x48, 0xdc, 0xdd, 0x2d, 0x71, 0xef, 0x49,
+ 0x89, 0xfb, 0x5b, 0x25, 0xfe, 0x00, 0x93, 0x8f, 0x8c, 0xa7, 0x98, 0x44, 0x18, 0x8b, 0x32, 0x79,
+ 0xc4, 0x96, 0x87, 0x30, 0x88, 0x90, 0x49, 0x91, 0xbb, 0xe7, 0x39, 0x0a, 0x3e, 0xc3, 0x74, 0xb5,
+ 0xa4, 0xbd, 0xd1, 0x1c, 0x9e, 0x35, 0x67, 0xd6, 0x2e, 0x21, 0xd6, 0x25, 0xcd, 0x54, 0xd4, 0x2e,
+ 0x3c, 0xfd, 0xdb, 0x81, 0xb1, 0x9e, 0x73, 0xbe, 0xc4, 0x5c, 0x91, 0x63, 0xe8, 0x69, 0x20, 0xfb,
+ 0xb6, 0x71, 0x7d, 0x45, 0x7f, 0xcf, 0x46, 0x8c, 0xb0, 0xe4, 0x1d, 0x4c, 0x9a, 0xce, 0x23, 0x74,
+ 0x97, 0x1b, 0xdb, 0x6d, 0x0b, 0x98, 0xb6, 0x05, 0x7e, 0xa4, 0xf1, 0x95, 0xcd, 0x6c, 0x33, 0xc4,
+ 0x27, 0x98, 0xb6, 0x6d, 0x45, 0x7c, 0x37, 0x66, 0x8b, 0x4b, 0xeb, 0x41, 0xdb, 0x8c, 0x38, 0x87,
+ 0xf1, 0xea, 0x8e, 0xe4, 0xa5, 0xad, 0xdb, 0x70, 0x8f, 0x3f, 0xdb, 0x08, 0x9b, 0xce, 0xbb, 0x81,
+ 0xf9, 0xcd, 0xbc, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0x71, 0x6f, 0x12, 0x61, 0x7b, 0x04, 0x00,
+ 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -370,6 +523,7 @@ type SendAgentClient interface {
UpdateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*Empty, error)
ValidateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*ValidateConfigReply, error)
UseridByMobile(ctx context.Context, in *UseridByMobileParams, opts ...grpc.CallOption) (*UseridByMobileReply, error)
+ BatchSend(ctx context.Context, in *BatchSendParams, opts ...grpc.CallOption) (*BatchSendReply, error)
}
type sendAgentClient struct {
@@ -416,12 +570,22 @@ func (c *sendAgentClient) UseridByMobile(ctx context.Context, in *UseridByMobile
return out, nil
}
+func (c *sendAgentClient) BatchSend(ctx context.Context, in *BatchSendParams, opts ...grpc.CallOption) (*BatchSendReply, error) {
+ out := new(BatchSendReply)
+ err := c.cc.Invoke(ctx, "/apis.SendAgent/BatchSend", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
// SendAgentServer is the server API for SendAgent service.
type SendAgentServer interface {
Send(context.Context, *SendParams) (*Empty, error)
UpdateConfig(context.Context, *UpdateConfigParams) (*Empty, error)
ValidateConfig(context.Context, *UpdateConfigParams) (*ValidateConfigReply, error)
UseridByMobile(context.Context, *UseridByMobileParams) (*UseridByMobileReply, error)
+ BatchSend(context.Context, *BatchSendParams) (*BatchSendReply, error)
}
// UnimplementedSendAgentServer can be embedded to have forward compatible implementations.
@@ -440,6 +604,9 @@ func (*UnimplementedSendAgentServer) ValidateConfig(ctx context.Context, req *Up
func (*UnimplementedSendAgentServer) UseridByMobile(ctx context.Context, req *UseridByMobileParams) (*UseridByMobileReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method UseridByMobile not implemented")
}
+func (*UnimplementedSendAgentServer) BatchSend(ctx context.Context, req *BatchSendParams) (*BatchSendReply, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method BatchSend not implemented")
+}
func RegisterSendAgentServer(s *grpc.Server, srv SendAgentServer) {
s.RegisterService(&_SendAgent_serviceDesc, srv)
@@ -517,6 +684,24 @@ func _SendAgent_UseridByMobile_Handler(srv interface{}, ctx context.Context, dec
return interceptor(ctx, in, info, handler)
}
+func _SendAgent_BatchSend_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(BatchSendParams)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(SendAgentServer).BatchSend(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/apis.SendAgent/BatchSend",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(SendAgentServer).BatchSend(ctx, req.(*BatchSendParams))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
var _SendAgent_serviceDesc = grpc.ServiceDesc{
ServiceName: "apis.SendAgent",
HandlerType: (*SendAgentServer)(nil),
@@ -537,6 +722,10 @@ var _SendAgent_serviceDesc = grpc.ServiceDesc{
MethodName: "UseridByMobile",
Handler: _SendAgent_UseridByMobile_Handler,
},
+ {
+ MethodName: "BatchSend",
+ Handler: _SendAgent_BatchSend_Handler,
+ },
},
Streams: []grpc.StreamDesc{},
Metadata: "send_server.proto",
diff --git a/pkg/notify/rpc/apis/send_server.proto b/pkg/notify/rpc/apis/send_server.proto
index 3652b668ca..d5bc98880c 100644
--- a/pkg/notify/rpc/apis/send_server.proto
+++ b/pkg/notify/rpc/apis/send_server.proto
@@ -45,9 +45,27 @@ message ValidateConfigReply {
string msg = 2;
}
+message BatchSendParams {
+ repeated string Contacts = 1;
+ string Title = 2;
+ string Message = 3;
+ string Priority = 4;
+ string RemoteTemplate = 5;
+}
+
+message FailedRecord {
+ string Contact = 1;
+ string Reason = 2;
+}
+
+message BatchSendReply {
+ repeated FailedRecord FailedRecords = 1;
+}
+
service SendAgent {
rpc Send(SendParams) returns (Empty);
rpc UpdateConfig(UpdateConfigParams) returns (Empty);
rpc ValidateConfig(UpdateConfigParams) returns (ValidateConfigReply);
rpc UseridByMobile(UseridByMobileParams) returns (UseridByMobileReply);
+ rpc BatchSend (BatchSendParams) returns (BatchSendReply);
}
diff --git a/pkg/notify/rpc/send.go b/pkg/notify/rpc/send.go
index 23bfea9dca..6d5240e4d1 100644
--- a/pkg/notify/rpc/send.go
+++ b/pkg/notify/rpc/send.go
@@ -32,7 +32,7 @@ import (
"yunion.io/x/pkg/errors"
"yunion.io/x/onecloud/pkg/mcclient"
- _interface "yunion.io/x/onecloud/pkg/notify/interface"
+ notifyv2 "yunion.io/x/onecloud/pkg/notify"
"yunion.io/x/onecloud/pkg/notify/models"
"yunion.io/x/onecloud/pkg/notify/rpc/apis"
"yunion.io/x/onecloud/pkg/util/fileutils2"
@@ -49,13 +49,13 @@ const (
type SRpcService struct {
SendServices *ServiceMap
socketFileDir string
- configStore _interface.IServiceConfigStore
- templateStore _interface.ITemplateStore
+ configStore notifyv2.IServiceConfigStore
+ templateStore notifyv2.ITemplateStore
}
// NewSRpcService create a SRpcService
-func NewSRpcService(socketFileDir string, configStore _interface.IServiceConfigStore,
- tempalteStore _interface.ITemplateStore) *SRpcService {
+func NewSRpcService(socketFileDir string, configStore notifyv2.IServiceConfigStore,
+ tempalteStore notifyv2.ITemplateStore) *SRpcService {
return &SRpcService{
SendServices: NewServiceMap(),
socketFileDir: socketFileDir,
@@ -129,10 +129,36 @@ func (self *SRpcService) Send(ctx context.Context, contactType, contact, topic,
return nil
}
+func (self *SRpcService) BatchSend(ctx context.Context, contacts []string, contactType, topic, message, priority string) ([]*apis.FailedRecord, error) {
+ args, err := self.templateStore.NotifyFilter(contactType, topic, message)
+ if err != nil {
+ return nil, errors.Wrap(err, "templateStore.NotifyFilter")
+ }
+
+ batchSendParams := apis.BatchSendParams{
+ Contacts: contacts,
+ Title: args.Title,
+ Message: args.Message,
+ Priority: args.Priority,
+ RemoteTemplate: args.RemoteTemplate,
+ }
+
+ f := func(service *apis.SendNotificationClient) (interface{}, error) {
+ return service.BatchSend(ctx, &batchSendParams)
+ }
+
+ ret, err := self.execute(ctx, f, contactType)
+ if err != nil {
+ return nil, errors.Wrapf(err, "contactType '%s'", contactType)
+ }
+ reply := ret.(*apis.BatchSendReply)
+ return reply.FailedRecords, nil
+}
+
// RestartService can restart remote rpc server and pass config info.
// This function should be call immediately after init notify server firstly
// This function should be call immediately after accept the request about changing config.
-func (self *SRpcService) RestartService(ctx context.Context, config _interface.SConfig, serviceName string) {
+func (self *SRpcService) RestartService(ctx context.Context, config notifyv2.SConfig, serviceName string) {
_, err := self.restartWithConfig(ctx, serviceName, config)
if err != nil {
log.Debugf("restart service failed: %s", err)
diff --git a/pkg/notify/cache/doc.go b/pkg/notify/service/doc.go
similarity index 89%
rename from pkg/notify/cache/doc.go
rename to pkg/notify/service/doc.go
index 17645129dc..b10d2f1886 100644
--- a/pkg/notify/cache/doc.go
+++ b/pkg/notify/service/doc.go
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cache // import "yunion.io/x/onecloud/pkg/notify/cache"
+package service // import "yunion.io/x/onecloud/pkg/notify/service"
diff --git a/pkg/notify/service/handlers.go b/pkg/notify/service/handlers.go
new file mode 100644
index 0000000000..0a296ef079
--- /dev/null
+++ b/pkg/notify/service/handlers.go
@@ -0,0 +1,63 @@
+package service
+
+import (
+ "yunion.io/x/onecloud/pkg/appsrv"
+ "yunion.io/x/onecloud/pkg/appsrv/dispatcher"
+ "yunion.io/x/onecloud/pkg/cloudcommon/db"
+ "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
+ "yunion.io/x/onecloud/pkg/notify/models"
+ "yunion.io/x/onecloud/pkg/notify/oldmodels"
+)
+
+const (
+ API_VERSION = "v2"
+)
+
+func InitHandlers(app *appsrv.Application) {
+ db.InitAllManagers()
+
+ db.RegistUserCredCacheUpdater()
+
+ db.AddScopeResourceCountHandler(API_VERSION, app)
+
+ // Data migration
+ db.RegisterModelManager(oldmodels.NotificationManager)
+ db.RegisterModelManager(oldmodels.ContactManager)
+ db.RegisterModelManager(oldmodels.ConfigManager)
+ db.RegisterModelManager(oldmodels.TemplateManager)
+ db.RegisterModelManager(oldmodels.UserCacheManager)
+
+ taskman.AddTaskHandler(API_VERSION, app)
+ for _, manager := range []db.IModelManager{
+ taskman.TaskManager,
+ taskman.SubTaskManager,
+ taskman.TaskObjectManager,
+
+ db.UserCacheManager,
+ db.TenantCacheManager,
+ models.SubContactManager,
+ models.VerificationManager,
+ } {
+ db.RegisterModelManager(manager)
+ }
+ for _, manager := range []db.IModelManager{
+ db.OpsLog,
+ db.Metadata,
+
+ models.ReceiverManager,
+ models.NotificationManager,
+ models.ConfigManager,
+ models.TemplateManager,
+ } {
+ db.RegisterModelManager(manager)
+ handler := db.NewModelHandler(manager)
+ dispatcher.AddModelDispatcher(API_VERSION, app, handler)
+ }
+ for _, manager := range []db.IJointModelManager{
+ models.ReceiverNotificationManager,
+ } {
+ db.RegisterModelManager(manager)
+ handler := db.NewJointModelHandler(manager)
+ dispatcher.AddJointModelDispatcher(API_VERSION, app, handler)
+ }
+}
diff --git a/pkg/notify/service.go b/pkg/notify/service/service.go
similarity index 88%
rename from pkg/notify/service.go
rename to pkg/notify/service/service.go
index b74411c70a..d2b6cf9a20 100644
--- a/pkg/notify/service.go
+++ b/pkg/notify/service/service.go
@@ -12,10 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package notify
+package service
import (
- "context"
"os"
"time"
@@ -29,12 +28,11 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/cronman"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
common_options "yunion.io/x/onecloud/pkg/cloudcommon/options"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/notify/cache"
"yunion.io/x/onecloud/pkg/notify/models"
"yunion.io/x/onecloud/pkg/notify/options"
_ "yunion.io/x/onecloud/pkg/notify/policy"
"yunion.io/x/onecloud/pkg/notify/rpc"
+ _ "yunion.io/x/onecloud/pkg/notify/tasks"
)
func StartService() {
@@ -60,9 +58,6 @@ func StartService() {
db.EnsureAppInitSyncDB(applicaion, dbOpts, models.InitDB)
defer cloudcommon.CloseDB()
- // init cache
- cache.RegistUserCredCacheUpdater()
-
// init notify service
models.NotifyService = rpc.NewSRpcService(opts.SocketFileDir, models.ConfigManager, models.TemplateManager)
models.NotifyService.InitAll()
@@ -73,10 +68,7 @@ func StartService() {
cron.AddJobAtIntervals("UpdateServices", time.Duration(opts.UpdateInterval)*time.Minute, models.NotifyService.UpdateServices)
// wrapped func to resend notifications
- resend := func(ctx context.Context, userCred mcclient.TokenCredential, isStart bool) {
- models.ReSend(opts.ReSendScope)
- }
- cron.AddJobAtIntervals("ReSendNotifications", time.Duration(opts.ReSendScope)*time.Second, resend)
+ cron.AddJobAtIntervals("ReSendNotifications", time.Duration(opts.ReSendScope)*time.Second, models.NotificationManager.ReSend)
cron.Start()
app.ServeForever(applicaion, baseOpts)
diff --git a/pkg/notify/utils/doc.go b/pkg/notify/tasks/doc.go
similarity index 90%
rename from pkg/notify/utils/doc.go
rename to pkg/notify/tasks/doc.go
index 37fa0628ed..4a54e435f1 100644
--- a/pkg/notify/utils/doc.go
+++ b/pkg/notify/tasks/doc.go
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package utils // import "yunion.io/x/onecloud/pkg/notify/utils"
+package tasks // import "yunion.io/x/onecloud/pkg/notify/tasks"
diff --git a/pkg/notify/tasks/notifications_send_task.go b/pkg/notify/tasks/notifications_send_task.go
new file mode 100644
index 0000000000..ad313e8ee9
--- /dev/null
+++ b/pkg/notify/tasks/notifications_send_task.go
@@ -0,0 +1,148 @@
+package tasks
+
+import (
+ "context"
+ "fmt"
+ "strings"
+ "time"
+
+ "yunion.io/x/jsonutils"
+ "yunion.io/x/log"
+
+ apis "yunion.io/x/onecloud/pkg/apis/notify"
+ "yunion.io/x/onecloud/pkg/cloudcommon/db"
+ "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
+ "yunion.io/x/onecloud/pkg/notify/models"
+ "yunion.io/x/onecloud/pkg/util/logclient"
+)
+
+type NotificationSendTask struct {
+ taskman.STask
+}
+
+func init() {
+ taskman.RegisterTask(NotificationSendTask{})
+}
+
+func (self *NotificationSendTask) taskFailed(ctx context.Context, notification *models.SNotification, reason string, all bool) {
+ log.Errorf("fail to send notification %q", notification.GetId())
+ if all {
+ notification.SetStatus(self.UserCred, apis.NOTIFICATION_STATUS_FAILED, reason)
+ } else {
+ notification.SetStatus(self.UserCred, apis.NOTIFICATION_STATUS_PART_OK, reason)
+ }
+ notification.AddOne()
+ logclient.AddActionLogWithContext(ctx, notification, logclient.ACT_SEND_NOTIFICATION, reason, self.UserCred, false)
+ self.SetStageFailed(ctx, jsonutils.NewString(reason))
+}
+
+func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
+ notification := obj.(*models.SNotification)
+ if notification.Status == apis.NOTIFICATION_STATUS_OK {
+ self.SetStageComplete(ctx, nil)
+ return
+ }
+ rns, err := notification.ReceiverNotificationsNotOK()
+ if err != nil {
+ self.taskFailed(ctx, notification, "fail to fetch ReceiverNotifications", true)
+ return
+ }
+ notification.SetStatus(self.UserCred, apis.NOTIFICATION_STATUS_SENDING, "")
+
+ // sort out what needs to be sent
+ failedRecord := make([]string, 0)
+ sendFail := func(rn *models.SReceiverNotification, reason string) {
+ rn.AfterSend(ctx, false, reason)
+ failedRecord = append(failedRecord, fmt.Sprintf("%s: %s", rn.ReceiverID, reason))
+ }
+
+ // build contactMap
+ contactMap := make(map[string]*models.SReceiverNotification)
+ for i := range rns {
+ if len(rns[i].ReceiverID) == 0 {
+ contactMap[rns[i].Contact] = &rns[i]
+ continue
+ }
+ receiver, err := rns[i].Receiver()
+ if err != nil {
+ sendFail(&rns[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"))
+ 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()))
+ continue
+ }
+ if !enabled {
+ sendFail(&rns[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()))
+ continue
+ }
+ if !verified {
+ sendFail(&rns[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)
+ continue
+ }
+ contactMap[contact] = &rns[i]
+ }
+
+ if len(contactMap) == 0 {
+ self.taskFailed(ctx, notification, strings.Join(failedRecord, "; "), true)
+ }
+
+ // set status before send
+ now := time.Now()
+ contacts := make([]string, 0, len(contactMap))
+ for c, rn := range contactMap {
+ rn.BeforeSend(ctx, now)
+ contacts = append(contacts, c)
+ }
+
+ // send
+ ret, err := models.NotifyService.BatchSend(ctx, contacts, notification.ContactType, notification.Topic, notification.Message, notification.Priority)
+ if err != nil {
+ for _, rn := range contactMap {
+ rn.AfterSend(ctx, false, err.Error())
+ }
+ failedRecord = append(failedRecord, fmt.Sprintf("others: %s", err.Error()))
+ self.taskFailed(ctx, notification, strings.Join(failedRecord, "; "), true)
+ return
+ }
+
+ // check result
+ for _, fd := range ret {
+ rn := contactMap[fd.Contact]
+ rn.AfterSend(ctx, false, fd.Reason)
+ failedRecord = append(failedRecord, fmt.Sprintf("%s: %s", rn.ReceiverID, fd.Reason))
+ }
+ if len(failedRecord) == len(contacts) {
+ self.taskFailed(ctx, notification, strings.Join(failedRecord, "; "), true)
+ return
+ }
+ if len(failedRecord) > 0 {
+ self.taskFailed(ctx, notification, strings.Join(failedRecord, "; "), false)
+ return
+ }
+ log.Infof("successfully send notification %q", notification.GetId())
+ notification.SetStatus(self.UserCred, apis.NOTIFICATION_STATUS_OK, "")
+ logclient.AddActionLogWithContext(ctx, notification, logclient.ACT_SEND_NOTIFICATION, "", self.UserCred, true)
+ self.SetStageComplete(ctx, nil)
+}
diff --git a/pkg/notify/tasks/subcontact_pull_task.go b/pkg/notify/tasks/subcontact_pull_task.go
new file mode 100644
index 0000000000..e209a17006
--- /dev/null
+++ b/pkg/notify/tasks/subcontact_pull_task.go
@@ -0,0 +1,87 @@
+// Copyright 2019 Yunion
+//
+// 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 tasks
+
+import (
+ "context"
+ "fmt"
+
+ "yunion.io/x/jsonutils"
+ "yunion.io/x/log"
+ "yunion.io/x/pkg/utils"
+
+ apis "yunion.io/x/onecloud/pkg/apis/notify"
+ "yunion.io/x/onecloud/pkg/cloudcommon/db"
+ "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
+ "yunion.io/x/onecloud/pkg/notify/models"
+ "yunion.io/x/onecloud/pkg/util/logclient"
+)
+
+var PullContactType = []string{
+ apis.DINGTALK,
+ apis.FEISHU,
+ apis.WORKWX,
+}
+
+type SubcontactPullTask struct {
+ taskman.STask
+}
+
+func init() {
+ taskman.RegisterTask(SubcontactPullTask{})
+}
+
+func (self *SubcontactPullTask) taskFailed(ctx context.Context, receiver *models.SReceiver, reason string) {
+ log.Errorf("fail to pull subcontact of receiver %q: %s", receiver.Id, reason)
+ receiver.SetStatus(self.UserCred, apis.RECEIVER_STATUS_PULL_FAILED, reason)
+ logclient.AddActionLogWithContext(ctx, receiver, logclient.ACT_PULL_SUBCONTACT, reason, self.UserCred, false)
+ self.SetStageFailed(ctx, jsonutils.NewString(reason))
+}
+
+func (self *SubcontactPullTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
+ // pull contacts
+ receiver := obj.(*models.SReceiver)
+ if len(receiver.Mobile) == 0 {
+ self.SetStageComplete(ctx, nil)
+ return
+ }
+ enabledContactTypes, _ := receiver.GetEnabledContactTypes()
+ for _, cType := range enabledContactTypes {
+ if !utils.IsInStringArray(cType, PullContactType) {
+ continue
+ }
+ userid, err := models.NotifyService.ContactByMobile(ctx, receiver.Mobile, cType)
+ if err != nil {
+ reason := fmt.Sprintf("fail to get %s contact by mobile %q: %v", cType, receiver.Mobile, err)
+ self.taskFailed(ctx, receiver, reason)
+ return
+ }
+ receiver.SetContact(cType, userid)
+ receiver.MarkContactTypeVerified(cType)
+ }
+ receiver.SetContact(apis.WEBCONSOLE, receiver.Id)
+ receiver.MarkContactTypeVerified(apis.WEBCONSOLE)
+ // push cache
+ err := receiver.PushCache(ctx)
+ if err != nil {
+ reason := fmt.Sprintf("PushCache: %v", err)
+ self.taskFailed(ctx, receiver, reason)
+ return
+ }
+ // success
+ receiver.SetStatus(self.UserCred, apis.RECEIVER_STATUS_READY, "")
+ logclient.AddActionLogWithContext(ctx, receiver, logclient.ACT_PULL_SUBCONTACT, "", self.UserCred, true)
+ self.SetStageComplete(ctx, nil)
+}
diff --git a/pkg/notify/tasks/verification_send_task.go b/pkg/notify/tasks/verification_send_task.go
new file mode 100644
index 0000000000..5b7e52ffce
--- /dev/null
+++ b/pkg/notify/tasks/verification_send_task.go
@@ -0,0 +1,76 @@
+package tasks
+
+import (
+ "context"
+ "fmt"
+
+ "yunion.io/x/jsonutils"
+ "yunion.io/x/log"
+
+ api "yunion.io/x/onecloud/pkg/apis/notify"
+ "yunion.io/x/onecloud/pkg/cloudcommon/db"
+ "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
+ "yunion.io/x/onecloud/pkg/notify/models"
+ "yunion.io/x/onecloud/pkg/util/logclient"
+)
+
+type VerificationSendTask struct {
+ taskman.STask
+}
+
+func init() {
+ taskman.RegisterTask(VerificationSendTask{})
+}
+
+func (self *VerificationSendTask) taskFailed(ctx context.Context, receiver *models.SReceiver, reason string) {
+ log.Errorf("fail to send verification: %s", reason)
+ logclient.AddActionLogWithContext(ctx, receiver, logclient.ACT_SEND_VERIFICATION, reason, self.UserCred, false)
+ self.SetStageFailed(ctx, jsonutils.NewString(reason))
+}
+
+func (self *VerificationSendTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
+ receiver := obj.(*models.SReceiver)
+ contactType, _ := self.Params.GetString("contact_type")
+ verification, err := models.VerificationManager.Get(receiver.GetId(), contactType)
+ if err != nil {
+ self.taskFailed(ctx, receiver, fmt.Sprintf("VerificationManager.Get for receiver_id %q and contact_type %q: %s", receiver.GetId(), contactType, err.Error()))
+ return
+ }
+ contact, err := receiver.GetContact(contactType)
+ if err != nil {
+ self.taskFailed(ctx, receiver, fmt.Sprintf("fail to get contact(type: %s): %s", contactType, err.Error()))
+ return
+ }
+
+ // build message
+ var message string
+ switch contactType {
+ case api.EMAIL:
+ info, err := models.TemplateManager.GetCompanyInfo(ctx)
+ if err != nil {
+ self.taskFailed(ctx, receiver, fmt.Sprintf("fail to get company info: %s", err.Error()))
+ return
+ }
+ data := struct {
+ models.SCompanyInfo
+ Name string
+ Code string
+ }{
+ Name: receiver.Name,
+ Code: verification.Token,
+ SCompanyInfo: info,
+ }
+ message = jsonutils.Marshal(data).String()
+ case api.MOBILE:
+ message = fmt.Sprintf(`{"code": "%s"}`, verification.Token)
+ default:
+ // no way
+ }
+ err = models.NotifyService.Send(ctx, contactType, contact, "verify", message, "")
+ if err != nil {
+ self.taskFailed(ctx, receiver, err.Error())
+ return
+ }
+ logclient.AddActionLogWithContext(ctx, receiver, logclient.ACT_SEND_VERIFICATION, "", self.UserCred, true)
+ self.SetStageComplete(ctx, nil)
+}
diff --git a/pkg/notify/utils/keystone.go b/pkg/notify/utils/keystone.go
deleted file mode 100644
index e512b298ec..0000000000
--- a/pkg/notify/utils/keystone.go
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 utils
-
-import (
- "context"
-
- "yunion.io/x/pkg/errors"
- "yunion.io/x/sqlchemy"
-
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/notify/cache"
-)
-
-func GetUserByIDOrName(ctx context.Context, idStr string) (*cache.SUser, error) {
- return cache.UserCacheManager.FetchUserByIDOrName(ctx, idStr)
-}
-
-func GetUsersWithoutRemote(ctx context.Context, idStr []string) ([]cache.SUser, error) {
- q := cache.UserCacheManager.Query()
- q = q.Filter(sqlchemy.OR(sqlchemy.In(q.Field("id"), idStr), sqlchemy.In(q.Field("name"), idStr))).Desc("updated_at")
- return cache.UserCacheManager.FetchUserFromLoaclCache(ctx, q)
-}
-
-func DeleteUsers(ctx context.Context, userCred mcclient.TokenCredential, ids []string) error {
- users, err := GetUsersWithoutRemote(ctx, ids)
- if err != nil {
- return err
- }
- for i := range users {
- err := users[i].Delete(ctx, userCred)
- if err != nil {
- return errors.Wrapf(err, "delete cache.SUser %s error", users[i].Id)
- }
- }
- return nil
-}
-
-func GetUserIdsLikeName(ctx context.Context, name string) ([]string, error) {
- users, err := cache.UserCacheManager.FetchUserLikeName(ctx, name, true)
- if err != nil {
- return nil, err
- }
- ret := make([]string, len(users))
- for i := range users {
- ret[i] = users[i].Id
- }
- return ret, nil
-}
-
-func GetUsersByGroupID(ctx context.Context, gid string) ([]string, error) {
- ret, err := cache.UserGroupCacheManager.FetchByGroupId(ctx, gid)
- if err != nil {
- return nil, err
- }
- ids := make([]string, len(ret))
- for i := range ret {
- ids[i] = ret[i].UserId
- }
- return ids, nil
-}
-
-func GetUsernameByID(ctx context.Context, id string) (string, error) {
- user, err := GetUserByIDOrName(ctx, id)
- if err != nil {
- return "", err
- }
- return user.Name, nil
-}
diff --git a/pkg/notify/utils/others.go b/pkg/notify/utils/others.go
deleted file mode 100644
index db07d4448b..0000000000
--- a/pkg/notify/utils/others.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 utils
-
-import (
- "bytes"
- "context"
- "fmt"
- "math/rand"
- "time"
-
- "yunion.io/x/jsonutils"
-
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/httperrors"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/util/rbacutils"
-)
-
-func FetchOwnerId(ctx context.Context, manager db.IModelManager, userCred mcclient.TokenCredential, data jsonutils.JSONObject) (mcclient.IIdentityProvider, error) {
- var ownerId mcclient.IIdentityProvider
- var err error
- if manager.ResourceScope() != rbacutils.ScopeSystem {
- ownerId, err = manager.FetchOwnerId(ctx, data)
- if err != nil {
- return nil, httperrors.NewGeneralError(err)
- }
- }
- if ownerId == nil {
- ownerId = userCred
- }
- return ownerId, nil
-}
-
-func GenerateMobileToken() string {
- rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
- token := fmt.Sprintf("%06v", rnd.Int31n(1000000))
- return token
-}
-
-func GenerateEmailToken(tokenLen int) string {
- rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
- token := new(bytes.Buffer)
- for token.Len() < tokenLen {
- token.WriteString(fmt.Sprintf("%x", rnd.Int31()))
- }
- return token.String()[:tokenLen]
-}
-
-func JsonArrayToStringArray(src []jsonutils.JSONObject) []string {
- des := make([]string, len(src))
- for i := range src {
- des[i], _ = src[i].GetString()
- }
- return des
-}
diff --git a/pkg/notify/utils/utils_test.go b/pkg/notify/utils/utils_test.go
deleted file mode 100644
index 08eff8c86c..0000000000
--- a/pkg/notify/utils/utils_test.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2019 Yunion
-//
-// 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 utils
-
-import (
- "testing"
-)
-
-func TestGenEmailToken(t *testing.T) {
- s := GenerateEmailToken(32)
- if len(s) != 32 {
- t.Error("email token length should be 32")
- }
-}
-
-func TestGenMobileToke(t *testing.T) {
- s := GenerateMobileToken()
- if len(s) != 6 {
- t.Errorf("mobile token length should be 6")
- }
-}
diff --git a/pkg/util/logclient/consts.go b/pkg/util/logclient/consts.go
index 1acdeab728..1678e749a4 100644
--- a/pkg/util/logclient/consts.go
+++ b/pkg/util/logclient/consts.go
@@ -205,4 +205,8 @@ const (
ACT_UPDATE_BILLING_OPTIONS = "更新账单文件"
ACT_UPDATE_CREDENTIAL = "更新账号密码"
+
+ ACT_PULL_SUBCONTACT = "拉取联系方式"
+ ACT_SEND_NOTIFICATION = "发送通知消息"
+ ACT_SEND_VERIFICATION = "发送验证消息"
)
diff --git a/scripts/codegen.py b/scripts/codegen.py
index 091a7c8877..bbf849f1f9 100755
--- a/scripts/codegen.py
+++ b/scripts/codegen.py
@@ -56,6 +56,8 @@ def run_swagger_yaml(svc, swagger_pkg_dir, output_dir):
cmd.extend(["-o", pjoin(output_dir, "swagger_%s.yaml" % svc)])
run_cmd(cmd)
+def remove_prefix(text, prefix):
+ return text[text.startswith(prefix) and len(prefix):]
class FuncDispatcher(object):
@@ -71,7 +73,7 @@ class FuncDispatcher(object):
func = getattr(self, attr)
if not callable(func):
continue
- svc = attr.lstrip('gen_')
+ svc = remove_prefix(attr, 'gen_')
gen_dict[svc] = func
self.gen_dict = gen_dict
@@ -131,6 +133,7 @@ class ModelAPI(FuncDispatcher):
self.run_model("compute")
self.run_model("image")
self.run_model("cloudid")
+ self.run_model("notify")
def gen_monitor(self):
self.run(pkg=["monitor", "models"], out=["monitor"])
@@ -174,6 +177,8 @@ class SwaggerCode(FuncDispatcher):
def gen_monitor(self):
self.run("monitor", pkg=["models"], out="monitor")
+ def gen_notify(self):
+ self.run("notify", pkg=["models"], out="notify")
class SwaggerYAML(FuncDispatcher):
@@ -204,6 +209,8 @@ class SwaggerYAML(FuncDispatcher):
def gen_monitor(self):
self.run("monitor")
+ def gen_notify(self):
+ self.run("notify")
class SwaggerServe(object):