feat(notify): advanced notify config and robot

1. Notify config has now become a domain resource, so that each domain can have its own configuration about notify.
   The domains has no configuration will use system config.
2. Robot separates from config and becomes a shared project resource. Sending information to robot can
   choose which robot to send to.
This commit is contained in:
rainzm
2021-03-09 19:29:27 +08:00
parent c22c9eb021
commit 1d1bb6c381
33 changed files with 2030 additions and 438 deletions
+8 -113
View File
@@ -15,121 +15,16 @@
package notifyv2
import (
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/cmd/climc/shell"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
options "yunion.io/x/onecloud/pkg/mcclient/options/notify"
)
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
})
type ConfigGetTypesOptions struct {
Robot string `json:"robot" choices:"yes|no|only"`
}
R(&ConfigGetTypesOptions{}, "notify-config-get-types", "Get all Config types", func(s *mcclient.ClientSession, args *ConfigGetTypesOptions) error {
param := jsonutils.Marshal(args)
result, err := modules.NotifyReceiver.PerformClassAction(s, "get-types", param)
if err != nil {
return err
}
printObject(result)
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
cmd := shell.NewResourceCmd(&modules.NotifyConfig).WithKeyword("notify-config")
cmd.List(new(options.ConfigListOptions))
cmd.Create(new(options.ConfigCreateOptions))
cmd.Update(new(options.ConfigUpdateOptions))
cmd.Show(new(options.ConfigOptions))
cmd.Delete(new(options.ConfigOptions))
}
+3
View File
@@ -27,6 +27,7 @@ import (
func init() {
type NotificationCreateInput struct {
Receivers []string `help:"ID or Name of Receiver"`
Robots []string `help:"ID or Name of Robot"`
ContactType string `help:"Contact type of receiver"`
TOPIC string `help:"Topic"`
Priority string `help:"Priority"`
@@ -41,6 +42,7 @@ func init() {
if args.Oldsdk {
msg := notify.SNotifyMessage{
Uid: args.Receivers,
Robots: args.Robots,
ContactType: notify.TNotifyChannel(args.ContactType),
Topic: args.TOPIC,
Priority: notify.TNotifyPriority(args.Priority),
@@ -55,6 +57,7 @@ func init() {
} else {
input := api.NotificationCreateInput{
Receivers: args.Receivers,
Robots: args.Robots,
ContactType: args.ContactType,
Topic: args.TOPIC,
Priority: args.Priority,
+1
View File
@@ -32,4 +32,5 @@ func init() {
cmd.Perform("trigger-verify", new(options.ReceiverTriggerVerifyOptions))
cmd.Perform("verify", new(options.ReceiverVerifyOptions))
cmd.PerformClass("intellij-get", new(options.ReceiverIntellijGetOptions))
cmd.PerformClass("get-types", new(options.ReceiverGetTypeOptions))
}
+32
View File
@@ -0,0 +1,32 @@
// 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/onecloud/cmd/climc/shell"
"yunion.io/x/onecloud/pkg/mcclient/modules"
options "yunion.io/x/onecloud/pkg/mcclient/options/notify"
)
func init() {
cmd := shell.NewResourceCmd(&modules.NotifyRobot).WithKeyword("notify-robot")
cmd.List(new(options.RobotListOptions))
cmd.Create(new(options.RobotCreateOptions))
cmd.Update(new(options.RobotUpdateOptions))
cmd.Show(new(options.RobotOptions))
cmd.Delete(new(options.RobotOptions))
cmd.Perform("enable", new(options.RobotOptions))
cmd.Perform("disable", new(options.RobotOptions))
}
+14 -5
View File
@@ -22,6 +22,7 @@ import (
type ConfigCreateInput struct {
apis.StandaloneResourceCreateInput
apis.DomainizedResourceInput
// description: config type
// required: true
@@ -32,6 +33,12 @@ type ConfigCreateInput struct {
// required: true
// example: {"app_id": "123456", "app_secret": "feishu_nihao"}
Content jsonutils.JSONObject `json:"content"`
// description: attribution
// required: true
// enum: system,domain
// example: system
Attribution string `json:"attribution"`
}
type ConfigUpdateInput struct {
@@ -43,13 +50,16 @@ type ConfigUpdateInput struct {
type ConfigDetails struct {
apis.StandaloneResourceDetails
apis.DomainizedResourceInfo
SConfig
}
type ConfigListInput struct {
apis.StandaloneResourceListInput
Type string `json:"type"`
apis.DomainizedResourceListInput
Type string `json:"type"`
Attribution string `json:"attribution"`
}
type ConfigValidateInput struct {
@@ -70,10 +80,9 @@ type ConfigValidateOutput struct {
}
type ConfigManagerGetTypesInput struct {
// description: Filter about robot
// enum: no,yes,only
// example: yes
Robot string `json:"robot"`
// description: domain where in config
// example: default
Domain string `json:"domain"`
}
type ConfigManagerGetTypesOutput struct {
+14
View File
@@ -70,6 +70,20 @@ const (
CTYPE_ROBOT_YES = "yes"
CTYPE_ROBOT_ONLY = "only"
CONFIG_ATTRIBUTION_SYSTEM = "system"
CONFIG_ATTRIBUTION_DOMAIN = "domain"
ROBOT_TYPE_FEISHU = "feishu"
ROBOT_TYPE_DINGTALK = "dingtalk"
ROBOT_TYPE_WORKWX = "workwx"
ROBOT_TYPE_WEBHOOK = "webhook"
ROBOT_STATUS_READY = "ready"
RECEIVER_TYPE_USER = "user"
RECEIVER_TYPE_CONTACT = "contact"
RECEIVER_TYPE_ROBOT = "robot"
SUBSCRIPTION_RESOURCE_CREATE_DELETE = "resource create delete"
SUBSCRIPTION_RESOURCE_CHANGECONFIG = "resource change config"
SUBSCRIPTION_RESOURCE_UPDATE = "resource udpate"
+3
View File
@@ -32,6 +32,9 @@ type NotificationCreateInput struct {
// description: direct contact, admin privileges required
// required: false
Contacts []string `json:"contacts"`
// description: robots
// example: feishu robot
Robots []string `json:"robots"`
// description: contact type
// required: ture
// example: email
+43
View File
@@ -0,0 +1,43 @@
package notify
import "yunion.io/x/onecloud/pkg/apis"
type RobotCreateInput struct {
apis.SharableVirtualResourceCreateInput
// description: robot type
// enum: feishu,dingtalk,workwx,webhook
// example: webhook
Type string `json:"type"`
// description: address
// example: http://helloworld.io/test/webhook
Address string `json:"address"`
// description: Language preference
// example: zh_CN
Lang string `json:"lang"`
}
type RobotDetails struct {
apis.SharableVirtualResourceDetails
}
type RobotListInput struct {
apis.SharableVirtualResourceListInput
apis.EnabledResourceBaseListInput
// description: robot type
// enum: feishu,dingtalk,workwx,webhook
// example: webhook
Type string `json:"type"`
// description: Language preference
// example: en
Lang string `json:"lang"`
}
type RobotUpdateInput struct {
apis.SharableVirtualResourceBaseUpdateInput
// description: address
// example: http://helloworld.io/test/webhook
Address string `json:"address"`
// description: Language preference
// example: en
Lang string `json:"lang"`
}
+13 -6
View File
@@ -119,22 +119,29 @@ func NotifyCriticalWithCtx(ctx context.Context, recipientId []string, isGroup bo
// NotifyAllWithoutRobot will send messages via all contacnt type from exclude robot contact type such as dingtalk-robot.
func NotifyAllWithoutRobot(recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
return notifyRobot(context.Background(), "no", recipientId, isGroup, priority, event, data)
return notifyAll(context.Background(), recipientId, isGroup, priority, event, data)
}
// NotifyAllWithoutRobot will send messages via all contacnt type from exclude robot contact type such as dingtalk-robot.
func NotifyAllWithoutRobotWithCtx(ctx context.Context, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
return notifyRobot(ctx, "no", recipientId, isGroup, priority, event, data)
return notifyAll(ctx, recipientId, isGroup, priority, event, data)
}
// NotifyRobot will send messages via all robot contact type such as dingtalk-robot.
func NotifyRobot(recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
return notifyRobot(context.Background(), "only", recipientId, isGroup, priority, event, data)
func NotifyRobot(robotIds []string, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
return NotifyRobotWithCtx(context.Background(), robotIds, priority, event, data)
}
// NotifyRobot will send messages via all robot contact type such as dingtalk-robot.
func NotifyRobotWithCtx(ctx context.Context, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
return notifyRobot(ctx, "only", recipientId, isGroup, priority, event, data)
func NotifyRobotWithCtx(ctx context.Context, robotIds []string, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
rawNotify(context.Background(), sNotifyParams{
robots: robotIds,
channel: npk.NotifyByRobot,
priority: priority,
event: event,
data: data,
})
return nil
}
func SystemNotify(priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) {
@@ -57,13 +57,12 @@ func systemNotify(ctx context.Context, priority npk.TNotifyPriority, event strin
notify(ctx, notifyAdminGroups, true, priority, event, data)
}
func notifyRobot(ctx context.Context, robot string, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
func notifyAll(ctx context.Context, recipientId []string, isGroup bool, priority npk.TNotifyPriority, event string, data jsonutils.JSONObject) error {
s, err := AdminSessionGenerator(ctx, consts.GetRegion(), "")
if err != nil {
return err
}
params := jsonutils.NewDict()
params.Set("robot", jsonutils.NewString(robot))
result, err := modules.NotifyReceiver.PerformClassAction(s, "get-types", params)
if err != nil {
return err
@@ -85,6 +84,30 @@ func notifyRobot(ctx context.Context, robot string, recipientId []string, isGrou
type sTarget struct {
reIds []string
contacts []string
robots []string
}
func langRobot(ctx context.Context, robots []string) (map[language.Tag]*sTarget, error) {
contextLang := i18n.Lang(ctx)
robotLang, err := getRobotLang(robots)
if err != nil {
return nil, err
}
ret := make(map[language.Tag]*sTarget)
for robot, langStr := range robotLang {
lang, err := language.Parse(langStr)
if err != nil {
log.Errorf("can't parse %s to language.Tag: %v", langStr, err)
lang = contextLang
}
t := ret[lang]
if t == nil {
ret[lang] = &sTarget{}
t = ret[lang]
}
t.robots = append(t.robots, robot)
}
return ret, nil
}
func lang(ctx context.Context, contactType npk.TNotifyChannel, reIds []string, contacts []string) (map[language.Tag]*sTarget, error) {
@@ -175,6 +198,7 @@ func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage,
msg := npk.SNotifyMessage{}
msg.Uid = reIds
msg.Priority = p.priority
msg.Robots = p.robots
msg.Contacts = p.contacts
msg.ContactType = p.channel
msg.Topic = p.event
@@ -185,9 +209,15 @@ func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage,
return []npk.SNotifyMessage{msg}, nil
}
langMap, err := lang(ctx, p.channel, reIds, p.contacts)
if err != nil {
return nil, err
var langMap map[language.Tag]*sTarget
if p.channel == npk.NotifyByRobot {
langMap, err = langRobot(ctx, p.robots)
} else {
langMap, err = lang(ctx, p.channel, reIds, p.contacts)
if err != nil {
return nil, err
}
}
msgs := make([]npk.SNotifyMessage, 0, len(langMap))
@@ -196,6 +226,7 @@ func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage,
msg := npk.SNotifyMessage{}
msg.Uid = t.reIds
msg.Priority = p.priority
msg.Robots = p.robots
msg.Contacts = t.contacts
msg.ContactType = p.channel
topic, _ := getContent(langSuffix, p.event, "title", p.channel, p.data)
@@ -218,6 +249,7 @@ func genMsgViaLang(ctx context.Context, p sNotifyParams) ([]npk.SNotifyMessage,
type sNotifyParams struct {
recipientId []string
robots []string
isGroup bool
contacts []string
channel npk.TNotifyChannel
+23
View File
@@ -64,6 +64,29 @@ func getUserLang(uids []string) (map[string]string, error) {
return uidLang, nil
}
func getRobotLang(robots []string) (map[string]string, error) {
s, err := AdminSessionGenerator(context.Background(), consts.GetRegion(), "")
if err != nil {
return nil, err
}
robotLang := make(map[string]string)
if len(robots) > 0 {
params := jsonutils.NewDict()
params.Set("filter", jsonutils.NewString(fmt.Sprintf("id.in(%s)", strings.Join(robots, ","))))
params.Set("scope", jsonutils.NewString("system"))
ret, err := modules.NotifyRobot.List(s, params)
if err != nil {
return nil, err
}
for i := range ret.Data {
id, _ := ret.Data[i].GetString("id")
langStr, _ := ret.Data[i].GetString("lang")
robotLang[id] = langStr
}
}
return robotLang, nil
}
func init() {
templatesTableLock = &sync.Mutex{}
templatesTable = make(map[string]*template.Template)
+10 -1
View File
@@ -23,6 +23,7 @@ type ConfigsManager struct {
var (
NotifyReceiver modulebase.ResourceManager
NotifyConfig modulebase.ResourceManager
NotifyRobot modulebase.ResourceManager
Notification modulebase.ResourceManager
NotifyTemplate modulebase.ResourceManager
NotifySubscription modulebase.ResourceManager
@@ -41,11 +42,19 @@ func init() {
NotifyConfig = NewNotifyv2Manager(
"notifyconfig",
"notifyconfigs",
[]string{"Type", "Content"},
[]string{"Name", "Type", "Content", "Attribution", "Project_Domain"},
[]string{},
)
register(&NotifyConfig)
NotifyRobot = NewNotifyv2Manager(
"robot",
"robots",
[]string{"ID", "Name", "Type", "Address", "Lang"},
[]string{},
)
register(&NotifyRobot)
Notification = NewNotifyv2Manager(
"notification",
"notifications",
+1
View File
@@ -29,6 +29,7 @@ const (
NotifyByWebConsole = TNotifyChannel("webconsole")
NotifyByFeishu = TNotifyChannel("feishu")
NotifyByWorkwx = TNotifyChannel("workwx")
NotifyByRobot = TNotifyChannel("robot")
NotifyFeishuRobot = TNotifyChannel("feishu-robot")
NotifyByDingTalkRobot = TNotifyChannel("dingtalk-robot")
@@ -31,6 +31,7 @@ var (
type SNotifyMessage struct {
Uid []string `json:"uid,omitempty"`
Gid []string `json:"gid,omitempty"`
Robots []string `json:"robots,omitempty"`
ContactType TNotifyChannel `json:"contact_type,omitempty"`
Contacts []string `json:"contracts"`
Topic string `json:"topic,omitempty"`
@@ -46,6 +47,7 @@ type SNotifyMessage struct {
type SNotifyV2Message struct {
Receivers []string `json:"receivers"`
Contacts []string `json:"contacts"`
Robots []string `json:"robots"`
ContactType string `json:"contact_type"`
Topic string `json:"topic"`
Priority string `json:"priority"`
@@ -83,6 +85,7 @@ func (manager *NotificationManager) Send(s *mcclient.ClientSession, msg SNotifyM
v2msg := SNotifyV2Message{
Receivers: receiverIds,
Contacts: msg.Contacts,
Robots: msg.Robots,
ContactType: string(msg.ContactType),
Topic: msg.Topic,
Priority: string(msg.Priority),
+82
View File
@@ -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 (
"strings"
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
type ConfigListOptions struct {
options.BaseListOptions
Type string `json:"type"`
Attribution string `json:"attribution"`
}
func (cl *ConfigListOptions) Params() (jsonutils.JSONObject, error) {
return options.ListStructToParams(cl)
}
type ConfigCreateOptions struct {
NAME string `help:"Name of Config"`
Domain string `help:"which domain create for, required if attribution is 'domain'"`
Type string `help:"The type of config"`
Configs []string `help:"Config content, format: 'key:value'"`
Attribution string `help:"Attribution" choices:"system|domain"`
}
func (cc *ConfigCreateOptions) Params() (jsonutils.JSONObject, error) {
jo := jsonutils.Marshal(cc)
d := jo.(*jsonutils.JSONDict)
d.Remove("configs")
configs := jsonutils.NewDict()
for _, kv := range cc.Configs {
index := strings.IndexByte(kv, ':')
configs.Set(kv[:index], jsonutils.NewString(kv[index+1:]))
}
d.Set("content", configs)
return d, nil
}
type ConfigOptions struct {
ID string
}
func (c *ConfigOptions) GetId() string {
return c.ID
}
func (c *ConfigOptions) Params() (jsonutils.JSONObject, error) {
return nil, nil
}
type ConfigUpdateOptions struct {
ConfigOptions
Configs []string
}
func (cu *ConfigUpdateOptions) Params() (jsonutils.JSONObject, error) {
configs := jsonutils.NewDict()
for _, kv := range cu.Configs {
index := strings.IndexByte(kv, ':')
configs.Set(kv[:index], jsonutils.NewString(kv[index+1:]))
}
d := jsonutils.NewDict()
d.Set("content", configs)
return d, nil
}
+9 -1
View File
@@ -31,7 +31,7 @@ func (rc *ReceiverCreateOptions) Params() (jsonutils.JSONObject, error) {
d := jsonutils.NewDict()
d.Set("uid", jsonutils.NewString(rc.UID))
d.Set("email", jsonutils.NewString(rc.Email))
d.Set("enabled_contact_type", jsonutils.NewStringArray(rc.EnabledContactTypes))
d.Set("enabled_contact_types", jsonutils.NewStringArray(rc.EnabledContactTypes))
d.Add(jsonutils.NewString(rc.Mobile), "international_mobile", "mobile")
d.Add(jsonutils.NewString(rc.MobileAreaCode), "international_mobile", "area_code")
return d, nil
@@ -110,3 +110,11 @@ type ReceiverIntellijGetOptions struct {
func (ri *ReceiverIntellijGetOptions) Params() (jsonutils.JSONObject, error) {
return jsonutils.Marshal(ri), nil
}
type ReceiverGetTypeOptions struct {
Domain string `help:"Domain under where available contact methods"`
}
func (rg *ReceiverGetTypeOptions) Params() (jsonutils.JSONObject, error) {
return jsonutils.Marshal(rg), nil
}
+69
View File
@@ -0,0 +1,69 @@
// 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/options"
)
type RobotListOptions struct {
options.BaseListOptions
Lang string
Type string `choices:"feishu|dingtalk|workwx|webhook"`
Enabled bool
}
func (rl *RobotListOptions) Params() (jsonutils.JSONObject, error) {
return options.ListStructToParams(rl)
}
type RobotCreateOptions struct {
NAME string
Type string `choices:"feishu|dingtalk|workwx|webhook"`
Address string
Lang string
}
func (rc *RobotCreateOptions) Params() (jsonutils.JSONObject, error) {
return jsonutils.Marshal(rc), nil
}
type RobotOptions struct {
ID string `help:"Id or Name of robot"`
}
func (r *RobotOptions) GetId() string {
return r.ID
}
func (r *RobotOptions) Params() (jsonutils.JSONObject, error) {
return nil, nil
}
type RobotUpdateOptions struct {
RobotOptions
SrobotUpdateOptions
}
type SrobotUpdateOptions struct {
Address string
Lang string
}
func (ru *RobotUpdateOptions) Params() (jsonutils.JSONObject, error) {
return jsonutils.Marshal(ru.SrobotUpdateOptions), nil
}
+14 -5
View File
@@ -27,10 +27,13 @@ type INotifyService interface {
InitAll() error
StopAll()
UpdateServices(ctx context.Context, userCred mcclient.TokenCredential, isStart bool)
RestartService(ctx context.Context, config SConfig, serviceName string)
UpdateConfig(ctx context.Context, service string, config SConfig) error
Send(ctx context.Context, contactType string, args apis.SendParams) error
ContactByMobile(ctx context.Context, mobile, serviceName string) (string, error)
ContactByMobile(ctx context.Context, mobile, serviceName, domainId string) (string, error)
BatchSend(ctx context.Context, contactType string, args apis.BatchSendParams) ([]*apis.FailedRecord, error)
SendRobotMessage(ctx context.Context, rType string, receivers []*apis.SReceiver, title string, message string) ([]*apis.FailedRecord, error)
AddConfig(ctx context.Context, service string, config SConfig) error
DeleteConfig(ctx context.Context, service, domainId string) error
ValidateConfig(ctx context.Context, cType string, configs map[string]string) (isValid bool, message string, err error)
}
@@ -53,8 +56,11 @@ type SBatchSendParams struct {
}
type IServiceConfigStore interface {
GetConfig(serviceName string) (SConfig, error)
SetConfig(serviceName string, config SConfig) error
GetConfigs(service string) ([]SConfig, error)
GetConfig(service, domainId string) (SConfig, error)
SetConfig(service string, config SConfig) error
HasSystemConfig(service string) (bool, error)
BatchCheckConfig(service string, domainIds []string) ([]bool, error)
}
type SNotification struct {
@@ -70,7 +76,10 @@ type ITemplateStore interface {
FillWithTemplate(ctx context.Context, lang string, notification SNotification) (params apis.SendParams, err error)
}
type SConfig map[string]string
type SConfig struct {
Config map[string]string
DomainId string
}
var (
ErrNoSuchMobile = errors.Error("no such mobile")
+205 -29
View File
@@ -26,8 +26,10 @@ import (
"yunion.io/x/sqlchemy"
api "yunion.io/x/onecloud/pkg/apis/notify"
"yunion.io/x/onecloud/pkg/cloudcommon/consts"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/cloudcommon/policy"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
@@ -40,6 +42,7 @@ import (
type SConfigManager struct {
db.SStandaloneResourceBaseManager
db.SDomainizedResourceBaseManager
}
var ConfigManager *SConfigManager
@@ -58,9 +61,11 @@ func init() {
type SConfig struct {
db.SStandaloneResourceBase
db.SDomainizedResourceBase
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"`
Type string `width:"15" nullable:"false" create:"required" get:"domain" list:"domain"`
Content jsonutils.JSONObject `nullable:"false" create:"required" update:"domain" get:"domain" list:"domain"`
Attribution string `width:"8" nullable:"false" default:"system" get:"domain" list:"domain"`
}
func (cm *SConfigManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.ConfigCreateInput) (api.ConfigCreateInput, error) {
@@ -69,13 +74,29 @@ func (cm *SConfigManager) ValidateCreateData(ctx context.Context, userCred mccli
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, api.WEBHOOK}) {
log.Infof("input: %s", jsonutils.Marshal(input))
if len(input.ProjectDomainId) > 0 {
_, input.DomainizedResourceInput, err = db.ValidateDomainizedResourceInput(ctx, input.DomainizedResourceInput)
if err != nil {
return input, err
}
}
if !utils.IsInStringArray(input.Type, []string{api.EMAIL, api.MOBILE, api.DINGTALK, api.FEISHU, api.WEBCONSOLE, api.WORKWX}) {
return input, httperrors.NewInputParameterError("unkown type %q", input.Type)
}
if !utils.IsInStringArray(input.Attribution, []string{api.CONFIG_ATTRIBUTION_SYSTEM, api.CONFIG_ATTRIBUTION_DOMAIN}) {
return input, httperrors.NewInputParameterError("invalid attribution, need %q or %q", api.CONFIG_ATTRIBUTION_SYSTEM, api.CONFIG_ATTRIBUTION_DOMAIN)
}
if input.Attribution == api.CONFIG_ATTRIBUTION_SYSTEM {
allowScope := policy.PolicyManager.AllowScope(userCred, consts.GetServiceType(), ConfigManager.KeywordPlural(), policy.PolicyActionCreate)
if allowScope != rbacutils.ScopeSystem {
return input, httperrors.NewInputParameterError("No permission to set %q attribution", api.CONFIG_ATTRIBUTION_SYSTEM)
}
}
if input.Content == nil {
return input, httperrors.NewMissingParameterError("content")
}
config, err := cm.GetConfigByType(input.Type)
config, err := cm.Config(input.Type, input.ProjectDomainId)
if err == nil && config != nil {
return input, httperrors.NewDuplicateResourceError("duplicate type %q", input.Type)
}
@@ -104,6 +125,21 @@ func (cm *SConfigManager) ValidateCreateData(ctx context.Context, userCred mccli
return input, nil
}
func (c *SConfig) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
err := c.SStandaloneResourceBase.CustomizeCreate(ctx, userCred, ownerId, query, data)
if err != nil {
return err
}
if c.Attribution == api.CONFIG_ATTRIBUTION_DOMAIN || c.Attribution == "" {
c.Attribution = api.CONFIG_ATTRIBUTION_DOMAIN
c.DomainId, _ = data.GetString("project_domain_id")
if c.DomainId == "" {
c.DomainId = userCred.GetProjectDomainId()
}
}
return nil
}
func (c *SConfig) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ConfigUpdateInput) (api.ConfigUpdateInput, error) {
// validate
configs := make(map[string]string)
@@ -129,7 +165,18 @@ func (c *SConfig) ValidateUpdateData(ctx context.Context, userCred mcclient.Toke
}
func (c *SConfig) PostCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) {
err := c.StartRepullSubcontactTask(ctx, userCred)
c.SStandaloneResourceBase.PostCreate(ctx, userCred, ownerId, query, data)
configMap := make(map[string]string)
err := c.Content.Unmarshal(&configMap)
if err != nil {
log.Errorf("unable to unmarshal: %v", err)
return
}
NotifyService.AddConfig(ctx, c.Type, notifyv2.SConfig{
Config: configMap,
DomainId: c.DomainId,
})
err = c.StartRepullSubcontactTask(ctx, userCred)
if err != nil {
log.Errorf("unable to StartRepullSubcontactTask: %v", err)
}
@@ -143,13 +190,27 @@ func (c *SConfig) PostUpdate(ctx context.Context, userCred mcclient.TokenCredent
log.Errorf("unable to unmarshal: %v", err)
return
}
NotifyService.RestartService(ctx, configMap, c.Type)
NotifyService.UpdateConfig(ctx, c.Type, notifyv2.SConfig{
Config: configMap,
DomainId: c.DomainId,
})
err = c.StartRepullSubcontactTask(ctx, userCred)
if err != nil {
log.Errorf("unable to StartRepullSubcontactTask: %v", err)
}
}
func (c *SConfig) PreDelete(ctx context.Context, userCred mcclient.TokenCredential) {
c.SStandaloneResourceBase.PreDelete(ctx, userCred)
NotifyService.DeleteConfig(ctx, c.Type, c.DomainId)
}
func (c *SConfig) PostDelete(ctx context.Context, userCred mcclient.TokenCredential) {
err := c.StartRepullSubcontactTask(ctx, userCred)
if err != nil {
log.Errorf("unable to StartRepullSubcontactTask: %v", err)
}
}
func (c *SConfig) StartRepullSubcontactTask(ctx context.Context, userCred mcclient.TokenCredential) error {
task, err := taskman.TaskManager.NewTask(ctx, "RepullSuncontactTask", c, userCred, nil, "", "")
if err != nil {
@@ -159,19 +220,8 @@ func (c *SConfig) StartRepullSubcontactTask(ctx context.Context, userCred mcclie
return nil
}
func (cm *SConfigManager) filterContactType(cTypes []string, robot string) []string {
switch robot {
case api.CTYPE_ROBOT_ONLY:
return intersection(cTypes, RobotContactTypes)
case api.CTYPE_ROBOT_YES:
return cTypes
default:
return difference(cTypes, RobotContactTypes)
}
}
var sortedCTypes = []string{
api.WEBCONSOLE, api.EMAIL, api.MOBILE, api.DINGTALK, api.FEISHU, api.WORKWX, api.DINGTALK_ROBOT, api.FEISHU_ROBOT, api.WORKWX_ROBOT,
api.WEBCONSOLE, api.EMAIL, api.MOBILE, api.DINGTALK, api.FEISHU, api.WORKWX,
}
func sortContactType(ctypes []string) []string {
@@ -185,6 +235,25 @@ func sortContactType(ctypes []string) []string {
return ret
}
func (cm *SConfigManager) availableContactTypes(domainId string) ([]string, error) {
q := cm.Query("type")
q = q.Filter(sqlchemy.OR(sqlchemy.AND(sqlchemy.Equals(q.Field("attribution"), api.CONFIG_ATTRIBUTION_DOMAIN), sqlchemy.Equals(q.Field("domain_id"), domainId)), sqlchemy.Equals(q.Field("attribution"), api.CONFIG_ATTRIBUTION_SYSTEM)))
allTypes := make([]struct {
Type string
}, 0, 3)
err := q.All(&allTypes)
if err != nil {
return nil, err
}
ret := make([]string, len(allTypes))
for i := range ret {
ret[i] = allTypes[i].Type
}
// De-duplication
return sets.NewString(ret...).UnsortedList(), nil
}
func (cm *SConfigManager) allContactType() ([]string, error) {
q := cm.Query("type")
allTypes := make([]struct {
@@ -206,10 +275,18 @@ func (self *SConfigManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQue
if err != nil {
return nil, err
}
q, err = self.SDomainizedResourceBaseManager.ListItemFilter(ctx, q, userCred, input.DomainizedResourceListInput)
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))
}
if len(input.Attribution) > 0 {
q = q.Equals("attribution", input.Attribution)
}
return q, nil
}
@@ -226,9 +303,11 @@ func (cm *SConfigManager) FetchCustomizeColumns(
isList bool,
) []api.ConfigDetails {
sRows := cm.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
dRows := cm.SDomainizedResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
rows := make([]api.ConfigDetails, len(objs))
for i := range rows {
rows[i].StandaloneResourceDetails = sRows[i]
rows[i].DomainizedResourceInfo = dRows[i]
}
return rows
}
@@ -238,6 +317,10 @@ func (cm *SConfigManager) QueryDistinctExtraField(q *sqlchemy.SQuery, field stri
if err != nil {
return q, nil
}
q, err = cm.SDomainizedResourceBaseManager.QueryDistinctExtraField(q, field)
if err != nil {
return q, nil
}
return q, nil
}
@@ -246,6 +329,10 @@ func (cm *SConfigManager) OrderByExtraFields(ctx context.Context, q *sqlchemy.SQ
if err != nil {
return nil, err
}
q, err = cm.SDomainizedResourceBaseManager.OrderByExtraFields(ctx, q, userCred, query.DomainizedResourceListInput)
if err != nil {
return nil, err
}
return q, nil
}
@@ -378,7 +465,18 @@ func (self *SConfigManager) InitializeData() error {
}
func (cm *SConfigManager) ResourceScope() rbacutils.TRbacScope {
return rbacutils.ScopeSystem
return rbacutils.ScopeDomain
}
func (cm *SConfigManager) FilterByOwner(q *sqlchemy.SQuery, owner mcclient.IIdentityProvider, scope rbacutils.TRbacScope) *sqlchemy.SQuery {
switch scope {
case rbacutils.ScopeDomain, rbacutils.ScopeProject:
q = q.Equals("attribution", api.CONFIG_ATTRIBUTION_DOMAIN)
if owner != nil {
q = q.Equals("domain_id", owner.GetProjectDomainId())
}
}
return q
}
func (cm *SConfigManager) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
@@ -398,36 +496,114 @@ func (c *SConfig) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCr
}
// Fetch all SConfig struct which type is contactType.
func (self *SConfigManager) GetConfigByType(contactType string) (*SConfig, error) {
var config SConfig
func (self *SConfigManager) Configs(contactType string) ([]SConfig, error) {
var configs = make([]SConfig, 0, 2)
q := self.Query()
q.Filter(sqlchemy.Equals(q.Field("type"), contactType))
err := q.All(&configs)
if err != nil {
return nil, errors.Wrapf(err, "fail to fetch SConfigs by type %s", contactType)
}
return configs, nil
}
func (self *SConfigManager) Config(contactType, domainId string) (*SConfig, error) {
q := self.Query()
q = q.Equals("type", contactType)
if len(domainId) == 0 {
q = q.Equals("attribution", api.CONFIG_ATTRIBUTION_SYSTEM)
} else {
q = q.Equals("domain_id", domainId).Equals("attribution", api.CONFIG_ATTRIBUTION_DOMAIN)
}
var config SConfig
err := q.First(&config)
if err != nil {
return nil, errors.Wrap(err, "fail to fetch SConfigs by type")
return nil, errors.Wrapf(err, "fail to fetch SConfig by type %s and domain %s", contactType, domainId)
}
return &config, nil
}
func (self *SConfigManager) GetConfig(contactType string) (notifyv2.SConfig, error) {
config, err := self.GetConfigByType(contactType)
func (self *SConfigManager) HasSystemConfig(contactType string) (bool, error) {
q := self.Query().Equals("type", contactType).Equals("attribution", "system")
c, err := q.CountWithError()
if err != nil {
return nil, err
return false, err
}
ret := make(map[string]string)
err = config.Content.Unmarshal(&ret)
return c > 0, nil
}
func (self *SConfigManager) BatchCheckConfig(contactType string, domainIds []string) ([]bool, error) {
domainIdSet := sets.NewString(domainIds...)
var configs = make([]SConfig, 0, 2)
q := self.Query().Equals("type", contactType).Equals("attribution", "domain").In("domain_id", domainIdSet.UnsortedList())
err := q.All(&configs)
if err != nil {
return nil, errors.Wrap(err, "fail unmarshal config content")
return nil, errors.Wrapf(err, "fail to fetch SConfigs by type %s", contactType)
}
for i := range configs {
if domainIdSet.Has(configs[i].DomainId) {
domainIdSet.Delete(configs[i].DomainId)
}
}
ret := make([]bool, len(domainIds))
for i := range domainIds {
if domainIdSet.Has(domainIds[i]) {
// no config of domainId, use default one
ret[i] = false
}
ret[i] = true
}
return ret, nil
}
func (self *SConfigManager) GetConfigs(contactType string) ([]notifyv2.SConfig, error) {
configs, err := self.Configs(contactType)
if err != nil {
return nil, err
}
ret := make([]notifyv2.SConfig, 0, len(configs))
for i := range configs {
c := make(map[string]string)
err := configs[i].Content.Unmarshal(&c)
if err != nil {
return nil, errors.Wrap(err, "fail unmarshal config content")
}
ret = append(ret, notifyv2.SConfig{
Config: c,
DomainId: configs[i].DomainId,
})
}
return ret, nil
}
func (self *SConfigManager) GetConfig(contactType, domainId string) (notifyv2.SConfig, error) {
config, err := self.Config(contactType, domainId)
if err != nil {
return notifyv2.SConfig{}, err
}
ret := make(map[string]string)
err = config.Content.Unmarshal(&ret)
if err != nil {
return notifyv2.SConfig{}, errors.Wrap(err, "fail unmarshal config content")
}
return notifyv2.SConfig{
Config: ret,
DomainId: config.DomainId,
}, nil
}
func (self *SConfigManager) SetConfig(contactType string, config notifyv2.SConfig) error {
content := jsonutils.Marshal(config)
content := jsonutils.Marshal(config.Config)
sConfig := &SConfig{
Type: contactType,
Content: content,
}
sConfig.DomainId = config.DomainId
if sConfig.DomainId == "" {
sConfig.Attribution = api.CONFIG_ATTRIBUTION_SYSTEM
} else {
sConfig.Attribution = api.CONFIG_ATTRIBUTION_DOMAIN
}
return self.TableSpec().InsertOrUpdate(context.Background(), sConfig)
}
+1
View File
@@ -33,6 +33,7 @@ func InitDB() error {
TemplateManager,
ReceiverNotificationManager,
SubscriptionManager,
RobotManager,
} {
err := manager.InitializeData()
if err != nil {
+40 -13
View File
@@ -80,25 +80,44 @@ const (
)
func (nm *SNotificationManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.NotificationCreateInput) (api.NotificationCreateInput, error) {
if len(input.Tag) == 0 && utils.IsInStringArray(input.Tag, []string{api.NOTIFICATION_TAG_ALERT}) {
if len(input.Tag) > 0 && !utils.IsInStringArray(input.Tag, []string{api.NOTIFICATION_TAG_ALERT}) {
return input, httperrors.NewInputParameterError("invalid tag")
}
if len(input.Receivers) == 0 {
if len(input.Contacts) > 0 {
if !userCred.IsAllow(rbacutils.ScopeSystem, api.SERVICE_TYPE, nm.KeywordPlural(), policy.PolicyActionPerform, SendByContact) {
return input, httperrors.NewForbiddenError("can't send notification by contact, need receiver")
return input, httperrors.NewForbiddenError("only admin can send notification by contact")
}
if len(input.Contacts) == 0 {
input.Contacts = []string{""}
}
}
log.Infof("notify input: %s", jsonutils.Marshal(input))
// check contact type enabled
allContactType, err := ConfigManager.allContactType()
if err != nil {
return input, err
}
if !utils.IsInStringArray(input.ContactType, allContactType) {
return input, httperrors.NewInputParameterError("Unconfigured contact type %q", input.ContactType)
// check robot
if len(input.Robots) > 0 {
input.ContactType = api.ROBOT
robots, err := RobotManager.FetchByIdOrNames(ctx, input.Robots...)
if err != nil {
return input, errors.Wrap(err, "RobotManager.FetchByIdOrNames")
}
idSet := sets.NewString()
nameSet := sets.NewString()
for i := range robots {
idSet.Insert(robots[i].Id)
nameSet.Insert(robots[i].Name)
}
for _, re := range input.Receivers {
if idSet.Has(re) || nameSet.Has(re) {
continue
}
if !input.IgnoreNonexistentReceiver {
return input, httperrors.NewInputParameterError("no such robot whose id is %q", re)
}
}
input.Robots = idSet.UnsortedList()
if len(input.Robots) == 0 {
return input, httperrors.NewInputParameterError("no valid receiver or contact")
}
}
// check receivers
if len(input.Receivers) > 0 {
@@ -138,10 +157,12 @@ func (nm *SNotificationManager) ValidateCreateData(ctx context.Context, userCred
length = len(input.Topic)
}
name := fmt.Sprintf("%s-%s-%s", input.Topic[:length], input.ContactType, nowStr)
var err error
input.Name, err = db.GenerateName(ctx, nm, ownerId, name)
if err != nil {
return input, errors.Wrapf(err, "unable to generate name for %s", name)
}
log.Infof("after validatecreate input: %s", jsonutils.Marshal(input))
return input, nil
}
@@ -160,9 +181,15 @@ func (n *SNotification) CustomizeCreate(ctx context.Context, userCred mcclient.T
}
}
for i := range input.Contacts {
_, err := ReceiverNotificationManager.CreateWithoutReceiver(ctx, userCred, input.Contacts[i], n.Id)
_, err := ReceiverNotificationManager.CreateContact(ctx, userCred, input.Contacts[i], n.Id)
if err != nil {
return errors.Wrap(err, "ReceiverNotificationManager.Create")
return errors.Wrap(err, "ReceiverNotificationManager.CreateContact")
}
}
for i := range input.Robots {
_, err := ReceiverNotificationManager.CreateRobot(ctx, userCred, input.Robots[i], n.Id)
if err != nil {
return errors.Wrap(err, "ReceiverNotificationManager.CreateRobot")
}
}
return nil
@@ -345,7 +372,7 @@ func (nm *SNotificationManager) create(ctx context.Context, userCred mcclient.To
}
}
for i := range contacts {
_, err := ReceiverNotificationManager.CreateWithoutReceiver(ctx, userCred, contacts[i], n.Id)
_, err := ReceiverNotificationManager.CreateContact(ctx, userCred, contacts[i], n.Id)
if err != nil {
return errors.Wrap(err, "ReceiverNotificationManager.Create")
}
+28 -8
View File
@@ -284,6 +284,10 @@ func (rm *SReceiverManager) ValidateCreateData(ctx context.Context, userCred mcc
return input, nil
}
func (r *SReceiver) IsEnabled() bool {
return r.Enabled.Bool()
}
var LaxMobileRegexp = regexp.MustCompile(`[0-9]{6,14}`)
func (r *SReceiver) IsEnabledContactType(ct string) (bool, error) {
@@ -689,13 +693,18 @@ func (r *SReceiverManager) AllowPerformGetTypes(ctx context.Context, userCred mc
return true
}
func (cm *SReceiverManager) PerformGetTypes(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ConfigManagerGetTypesInput) (api.ConfigManagerGetTypesOutput, error) {
func (rm *SReceiverManager) PerformGetTypes(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.ConfigManagerGetTypesInput) (api.ConfigManagerGetTypesOutput, error) {
output := api.ConfigManagerGetTypesOutput{}
allContactType, err := ConfigManager.allContactType()
t, err := db.TenantCacheManager.FetchDomainByIdOrName(ctx, input.Domain)
if err != nil {
return output, err
return output, errors.Wrap(err, "unable to FetchDomainByIdOrName")
}
output.Types = sortContactType(ConfigManager.filterContactType(allContactType, input.Robot))
ret, err := ConfigManager.availableContactTypes(t.Id)
if err != nil {
return output, errors.Wrap(err, "unable to get available contact types")
}
output.Types = sortContactType(ret)
return output, nil
}
@@ -1183,12 +1192,10 @@ func (rm *SReceiverManager) FetchByIDs(ctx context.Context, ids ...string) ([]SR
return contacts, nil
}
func (rm *SReceiverManager) FetchByIdOrNames(ctx context.Context, idOrNames ...string) ([]SReceiver, error) {
func idOrNameFilter(q *sqlchemy.SQuery, idOrNames ...string) *sqlchemy.SQuery {
if len(idOrNames) == 0 {
return nil, nil
return q
}
var err error
q := rm.Query()
var conds []sqlchemy.ICondition
for _, idOrName := range idOrNames {
conds = append(conds, sqlchemy.Equals(q.Field("name"), idOrName))
@@ -1201,6 +1208,15 @@ func (rm *SReceiverManager) FetchByIdOrNames(ctx context.Context, idOrNames ...s
} else if len(conds) > 1 {
q = q.Filter(sqlchemy.OR(conds...))
}
return q
}
func (rm *SReceiverManager) FetchByIdOrNames(ctx context.Context, idOrNames ...string) ([]SReceiver, error) {
if len(idOrNames) == 0 {
return nil, nil
}
var err error
q := idOrNameFilter(rm.Query(), idOrNames...)
receivers := make([]SReceiver, 0, len(idOrNames))
err = db.FetchModelObjects(rm, q, &receivers)
if err != nil {
@@ -1255,3 +1271,7 @@ func (r *SReceiver) GetContact(cType string) (string, error) {
}
return "", nil
}
func (r *SReceiver) GetDomainId() string {
return r.DomainId
}
+96 -3
View File
@@ -43,6 +43,7 @@ type SReceiverNotification struct {
NotificationID string `width:"128" charset:"ascii" nullable:"false" index:"true"`
// ignore if ReceiverID is not empty or default
Contact string `width:"128" nullable:"false" index:"true"`
ReceiverType string `width:"16"`
SendAt time.Time `nullable:"false"`
SendBy string `width:"128" nullable:"false"`
Status string `width:"36" charset:"ascii"`
@@ -57,6 +58,18 @@ func (rnm *SReceiverNotificationManager) Create(ctx context.Context, userCred mc
rn := &SReceiverNotification{
ReceiverID: receiverID,
NotificationID: notificationID,
ReceiverType: api.RECEIVER_TYPE_USER,
Status: api.RECEIVER_NOTIFICATION_RECEIVED,
SendBy: userCred.GetUserId(),
}
return rn, rnm.TableSpec().Insert(ctx, rn)
}
func (rnm *SReceiverNotificationManager) CreateRobot(ctx context.Context, userCred mcclient.TokenCredential, RobotID, notificationID string) (*SReceiverNotification, error) {
rn := &SReceiverNotification{
ReceiverID: RobotID,
NotificationID: notificationID,
ReceiverType: api.RECEIVER_TYPE_ROBOT,
Status: api.RECEIVER_NOTIFICATION_RECEIVED,
SendBy: userCred.GetUserId(),
}
@@ -71,10 +84,10 @@ func (rnm *SReceiverNotificationManager) GetSlaveFieldName() string {
return "receiver_id"
}
func (rnm *SReceiverNotificationManager) CreateWithoutReceiver(ctx context.Context, userCred mcclient.TokenCredential, contact, notificationID string) (*SReceiverNotification, error) {
func (rnm *SReceiverNotificationManager) CreateContact(ctx context.Context, userCred mcclient.TokenCredential, contact, notificationID string) (*SReceiverNotification, error) {
rn := &SReceiverNotification{
NotificationID: notificationID,
ReceiverID: ReceiverIdDefault,
ReceiverType: api.RECEIVER_TYPE_CONTACT,
Contact: contact,
Status: api.RECEIVER_NOTIFICATION_RECEIVED,
SendBy: userCred.GetUserId(),
@@ -82,7 +95,18 @@ func (rnm *SReceiverNotificationManager) CreateWithoutReceiver(ctx context.Conte
return rn, rnm.TableSpec().Insert(ctx, rn)
}
func (rn *SReceiverNotification) Receiver() (*SReceiver, error) {
// 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
// }
// receiver.SetModelManager(ReceiverManager, &receiver)
// return &receiver, nil
// }
func (rn *SReceiverNotification) receiver() (*SReceiver, error) {
q := ReceiverManager.Query().Equals("id", rn.ReceiverID)
var receiver SReceiver
err := q.First(&receiver)
@@ -93,6 +117,34 @@ func (rn *SReceiverNotification) Receiver() (*SReceiver, error) {
return &receiver, nil
}
func (rn *SReceiverNotification) robot() (*SRobot, error) {
q := RobotManager.Query().Equals("id", rn.ReceiverID)
var robot SRobot
err := q.First(&robot)
if err != nil {
return nil, err
}
robot.SetModelManager(RobotManager, &robot)
return &robot, nil
}
func (rn *SReceiverNotification) Receiver() (IReceiver, error) {
switch rn.ReceiverType {
case api.RECEIVER_TYPE_USER:
return rn.receiver()
case api.RECEIVER_TYPE_CONTACT:
return &SContact{contact: rn.Contact}, nil
case api.RECEIVER_TYPE_ROBOT:
return rn.robot()
default:
// compatible
if rn.ReceiverID != "" && rn.ReceiverID != ReceiverIdDefault {
return rn.receiver()
}
return &SContact{contact: rn.Contact}, nil
}
}
func (rn *SReceiverNotification) BeforeSend(ctx context.Context, sendTime time.Time) error {
if sendTime.IsZero() {
sendTime = time.Now()
@@ -117,3 +169,44 @@ func (rn *SReceiverNotification) AfterSend(ctx context.Context, success bool, re
})
return err
}
type IReceiver interface {
IsEnabled() bool
GetDomainId() string
IsEnabledContactType(string) (bool, error)
IsVerifiedContactType(string) (bool, error)
GetContact(string) (string, error)
GetTemplateLang(context.Context) (string, error)
}
type SReceiverBase struct {
}
func (s SReceiverBase) IsEnabled() bool {
return true
}
func (s SReceiverBase) GetDomainId() string {
return ""
}
func (s SReceiverBase) IsEnabledContactType(_ string) (bool, error) {
return true, nil
}
func (s SReceiverBase) IsVerifiedContactType(_ string) (bool, error) {
return true, nil
}
func (s SReceiverBase) GetTemplateLang(ctx context.Context) (string, error) {
return "", nil
}
type SContact struct {
SReceiverBase
contact string
}
func (s *SContact) GetContact(_ string) (string, error) {
return s.contact, nil
}
+282
View File
@@ -0,0 +1,282 @@
package models
import (
"context"
"strings"
"github.com/pkg/errors"
"golang.org/x/text/language"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/utils"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/apis"
idenapi "yunion.io/x/onecloud/pkg/apis/identity"
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"
rpcapi "yunion.io/x/onecloud/pkg/notify/rpc/apis"
"yunion.io/x/onecloud/pkg/util/rbacutils"
"yunion.io/x/onecloud/pkg/util/stringutils2"
)
type SRobotManager struct {
db.SSharableVirtualResourceBaseManager
db.SEnabledResourceBaseManager
}
var RobotManager *SRobotManager
func init() {
RobotManager = &SRobotManager{
SSharableVirtualResourceBaseManager: db.NewSharableVirtualResourceBaseManager(
SRobot{},
"robots_tbl",
"robot",
"robots",
),
}
RobotManager.SetVirtualObject(RobotManager)
}
type SRobot struct {
db.SSharableVirtualResourceBase
db.SEnabledResourceBase
Type string `width:"16" nullable:"false" create:"required" get:"user" list:"user" index:"true"`
Address string `nullable:"false" create:"required" update:"user" get:"user" list:"user"`
Lang string `width:"16" nullable:"false" create:"required" update:"user" get:"user" list:"user"`
}
func (rm *SRobotManager) InitializeData() error {
log.Infof("start to init data for notify robot")
// convert robot config
q := ConfigManager.Query().In("type", append(RobotContactTypes, api.WEBHOOK))
var configs []SConfig
err := db.FetchModelObjects(ConfigManager, q, &configs)
if err != nil {
return err
}
if len(configs) == 0 {
return nil
}
robots := make([]SRobot, len(configs))
for i := range configs {
webhook, _ := configs[i].Content.GetString("webhook")
robot := SRobot{
Address: webhook,
Lang: "zh_CN",
}
robot.IsPublic = true
robot.PublicScope = string(rbacutils.ScopeSystem)
robot.DomainId = idenapi.DEFAULT_DOMAIN_ID
robot.Status = api.RECEIVER_STATUS_READY
switch configs[i].Type {
case api.FEISHU_ROBOT:
robot.Type = api.ROBOT_TYPE_FEISHU
robot.Name = "Feishu Robot"
case api.DINGTALK_ROBOT:
robot.Type = api.ROBOT_TYPE_DINGTALK
robot.Name = "Dingtalk Robot"
case api.WORKWX_ROBOT:
robot.Type = api.ROBOT_TYPE_WORKWX
robot.Name = "Workwx Robot"
case api.WEBHOOK:
robot.Type = api.ROBOT_TYPE_WEBHOOK
robot.Name = "Webhook"
addresses := strings.Split(robot.Address, ",")
for i := 1; i < len(addresses); i++ {
robotn := robot
robotn.Address = strings.TrimSpace(addresses[i])
robots = append(robots, robotn)
}
robot.Address = addresses[0]
}
robots = append(robots, robot)
}
ctx := context.Background()
// insert new robot
for i := range robots {
err := rm.TableSpec().Insert(ctx, &robots[i])
if err != nil {
return err
}
}
// delete old configs
for i := range configs {
config := &configs[i]
_, err := db.Update(config, func() error {
return config.MarkDelete()
})
if err != nil {
return err
}
}
return nil
}
func (rm *SRobotManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input api.RobotCreateInput) (api.RobotCreateInput, error) {
var err error
input.SharableVirtualResourceCreateInput, err = rm.SSharableVirtualResourceBaseManager.ValidateCreateData(ctx, userCred, ownerId, query, input.SharableVirtualResourceCreateInput)
if err != nil {
return input, errors.Wrap(err, "SSharableVirtualResourceBaseManager.ValidateCreateData")
}
// check type
if !utils.IsInStringArray(input.Type, []string{api.ROBOT_TYPE_FEISHU, api.ROBOT_TYPE_WORKWX, api.ROBOT_TYPE_DINGTALK, api.ROBOT_TYPE_WEBHOOK}) {
return input, httperrors.NewInputParameterError("unkown type %q", input.Type)
}
// check lang
_, err = language.Parse(input.Lang)
if err != nil {
return input, httperrors.NewInputParameterError("invalid lang %q: %s", input.Lang, err.Error())
}
// check Address
records, err := NotifyService.SendRobotMessage(ctx, input.Type, []*rpcapi.SReceiver{
{
Contact: input.Address,
DomainId: input.ProjectDomainId,
},
}, "Validate", "This is a verification message, please ignore.")
if err != nil {
return input, errors.Wrap(err, "unable to validate address")
}
if len(records) > 0 {
return input, httperrors.NewInputParameterError("invalid address: %s", records[0].Reason)
}
return input, nil
}
func (r *SRobot) Receiver() *rpcapi.SReceiver {
return &rpcapi.SReceiver{
Contact: r.Address,
DomainId: r.DomainId,
}
}
func (rm *SRobotManager) FetchCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []interface{}, fields stringutils2.SSortedStrings, isList bool) []api.RobotDetails {
sRows := rm.SSharableVirtualResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
rows := make([]api.RobotDetails, len(objs))
for i := range rows {
rows[i].SharableVirtualResourceDetails = sRows[i]
}
return rows
}
func (rm *SRobotManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input api.RobotListInput) (*sqlchemy.SQuery, error) {
q, err := rm.SSharableBaseResourceManager.ListItemFilter(ctx, q, userCred, input.SharableResourceBaseListInput)
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.Type) > 0 {
q = q.Equals("type", input.Type)
}
if len(input.Lang) > 0 {
q = q.Equals("lang", input.Lang)
}
return q, nil
}
func (r *SRobot) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.RobotUpdateInput) (api.RobotUpdateInput, error) {
var err error
input.SharableVirtualResourceBaseUpdateInput, err = r.SSharableVirtualResourceBase.ValidateUpdateData(ctx, userCred, query, input.SharableVirtualResourceBaseUpdateInput)
if err != nil {
return input, errors.Wrap(err, "SSharableVirtualResourceBase.ValidateUpdateData")
}
// check lang
_, err = language.Parse(input.Lang)
if err != nil {
return input, httperrors.NewInputParameterError("invalid lang %q: %s", input.Lang, err.Error())
}
// check Address
records, err := NotifyService.SendRobotMessage(ctx, r.Type, []*rpcapi.SReceiver{
{
Contact: input.Address,
DomainId: r.DomainId,
},
}, "Validate", "This is a verification message, please ignore.")
if err != nil {
return input, errors.Wrap(err, "unable to validate address")
}
if len(records) > 0 {
return input, httperrors.NewInputParameterError("invalid address: %s", records[0].Reason)
}
return input, nil
}
func (r *SRobot) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
r.Status = api.ROBOT_STATUS_READY
return nil
}
func (rm *SRobotManager) FetchByIdOrNames(ctx context.Context, idOrNames ...string) ([]SRobot, error) {
if len(idOrNames) == 0 {
return nil, nil
}
var err error
q := idOrNameFilter(rm.Query(), idOrNames...)
robots := make([]SRobot, 0, len(idOrNames))
err = db.FetchModelObjects(rm, q, &robots)
if err != nil {
return nil, err
}
return robots, nil
}
func (r *SRobot) IsEnabled() bool {
return r.Enabled.Bool()
}
func (r *SRobot) IsEnabledContactType(ctype string) (bool, error) {
return ctype == api.ROBOT, nil
}
func (r *SRobot) IsVerifiedContactType(ctype string) (bool, error) {
return ctype == api.ROBOT, nil
}
func (r *SRobot) GetContact(ctype string) (string, error) {
return r.Address, nil
}
func (r *SRobot) GetTemplateLang(ctx context.Context) (string, error) {
lang, err := language.Parse(r.Lang)
if err != nil {
return "", errors.Wrapf(err, "unable to prase language %q", r.Lang)
}
tLang := notifyclientI18nTable.LookupByLang(lang, tempalteLang)
return tLang, nil
}
func (r *SRobot) GetDomainId() string {
return r.DomainId
}
func (r *SRobot) AllowPerformEnable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return r.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, r, "enable")
}
func (r *SRobot) 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 *SRobot) AllowPerformDisable(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return r.IsOwner(userCred) || db.IsAdminAllowPerform(userCred, r, "disable")
}
func (r *SRobot) 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
}
+26 -2
View File
@@ -41,13 +41,31 @@ func (c *SendNotificationClient) Send(ctx context.Context, in *SendParams, opts
return c.sendAgentClient.Send(ctx, in, opts...)
}
func (c *SendNotificationClient) UpdateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*Empty, error) {
func (c *SendNotificationClient) AddConfig(ctx context.Context, in *AddConfigInput, opts ...grpc.CallOption) (*Empty, error) {
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
defer cancel()
return c.sendAgentClient.AddConfig(ctx, in, opts...)
}
func (c *SendNotificationClient) DeleteConfig(ctx context.Context, in *DeleteConfigInput, opts ...grpc.CallOption) (*Empty, error) {
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
defer cancel()
return c.sendAgentClient.DeleteConfig(ctx, in, opts...)
}
func (c *SendNotificationClient) UpdateConfig(ctx context.Context, in *UpdateConfigInput, opts ...grpc.CallOption) (*Empty, error) {
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
defer cancel()
return c.sendAgentClient.UpdateConfig(ctx, in, opts...)
}
func (c *SendNotificationClient) ValidateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*ValidateConfigReply, error) {
func (c *SendNotificationClient) CompleteConfig(ctx context.Context, in *CompleteConfigInput, opts ...grpc.CallOption) (*Empty, error) {
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
defer cancel()
return c.sendAgentClient.CompleteConfig(ctx, in, opts...)
}
func (c *SendNotificationClient) ValidateConfig(ctx context.Context, in *ValidateConfigInput, opts ...grpc.CallOption) (*ValidateConfigReply, error) {
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
defer cancel()
return c.sendAgentClient.ValidateConfig(ctx, in, opts...)
@@ -59,6 +77,12 @@ func (c *SendNotificationClient) UseridByMobile(ctx context.Context, in *UseridB
return c.sendAgentClient.UseridByMobile(ctx, in, opts...)
}
func (c *SendNotificationClient) Ready(ctx context.Context, in *ReadyInput, opts ...grpc.CallOption) (*ReadyOutput, error) {
ctx, cancel := context.WithTimeout(ctx, c.CallTimeout)
defer cancel()
return c.sendAgentClient.Ready(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()
+572 -101
View File
@@ -6,12 +6,11 @@ package apis
import (
context "context"
fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
@@ -26,15 +25,15 @@ var _ = math.Inf
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type SendParams struct {
Contact string `protobuf:"bytes,1,opt,name=Contact,proto3" json:"Contact,omitempty"`
Topic string `protobuf:"bytes,2,opt,name=Topic,proto3" json:"Topic,omitempty"`
Title string `protobuf:"bytes,3,opt,name=Title,proto3" json:"Title,omitempty"`
Message string `protobuf:"bytes,4,opt,name=Message,proto3" json:"Message,omitempty"`
Priority string `protobuf:"bytes,5,opt,name=Priority,proto3" json:"Priority,omitempty"`
RemoteTemplate string `protobuf:"bytes,6,opt,name=RemoteTemplate,proto3" json:"RemoteTemplate,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
Receiver *SReceiver `protobuf:"bytes,1,opt,name=Receiver,proto3" json:"Receiver,omitempty"`
Topic string `protobuf:"bytes,2,opt,name=Topic,proto3" json:"Topic,omitempty"`
Title string `protobuf:"bytes,3,opt,name=Title,proto3" json:"Title,omitempty"`
Message string `protobuf:"bytes,4,opt,name=Message,proto3" json:"Message,omitempty"`
Priority string `protobuf:"bytes,5,opt,name=Priority,proto3" json:"Priority,omitempty"`
RemoteTemplate string `protobuf:"bytes,6,opt,name=RemoteTemplate,proto3" json:"RemoteTemplate,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SendParams) Reset() { *m = SendParams{} }
@@ -62,11 +61,11 @@ func (m *SendParams) XXX_DiscardUnknown() {
var xxx_messageInfo_SendParams proto.InternalMessageInfo
func (m *SendParams) GetContact() string {
func (m *SendParams) GetReceiver() *SReceiver {
if m != nil {
return m.Contact
return m.Receiver
}
return ""
return nil
}
func (m *SendParams) GetTopic() string {
@@ -104,47 +103,220 @@ func (m *SendParams) GetRemoteTemplate() string {
return ""
}
type UpdateConfigParams struct {
type ValidateConfigInput struct {
Configs map[string]string `protobuf:"bytes,1,rep,name=configs,proto3" json:"configs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *UpdateConfigParams) Reset() { *m = UpdateConfigParams{} }
func (m *UpdateConfigParams) String() string { return proto.CompactTextString(m) }
func (*UpdateConfigParams) ProtoMessage() {}
func (*UpdateConfigParams) Descriptor() ([]byte, []int) {
func (m *ValidateConfigInput) Reset() { *m = ValidateConfigInput{} }
func (m *ValidateConfigInput) String() string { return proto.CompactTextString(m) }
func (*ValidateConfigInput) ProtoMessage() {}
func (*ValidateConfigInput) Descriptor() ([]byte, []int) {
return fileDescriptor_63fdd68f7eb311f9, []int{1}
}
func (m *UpdateConfigParams) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UpdateConfigParams.Unmarshal(m, b)
func (m *ValidateConfigInput) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ValidateConfigInput.Unmarshal(m, b)
}
func (m *UpdateConfigParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_UpdateConfigParams.Marshal(b, m, deterministic)
func (m *ValidateConfigInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ValidateConfigInput.Marshal(b, m, deterministic)
}
func (m *UpdateConfigParams) XXX_Merge(src proto.Message) {
xxx_messageInfo_UpdateConfigParams.Merge(m, src)
func (m *ValidateConfigInput) XXX_Merge(src proto.Message) {
xxx_messageInfo_ValidateConfigInput.Merge(m, src)
}
func (m *UpdateConfigParams) XXX_Size() int {
return xxx_messageInfo_UpdateConfigParams.Size(m)
func (m *ValidateConfigInput) XXX_Size() int {
return xxx_messageInfo_ValidateConfigInput.Size(m)
}
func (m *UpdateConfigParams) XXX_DiscardUnknown() {
xxx_messageInfo_UpdateConfigParams.DiscardUnknown(m)
func (m *ValidateConfigInput) XXX_DiscardUnknown() {
xxx_messageInfo_ValidateConfigInput.DiscardUnknown(m)
}
var xxx_messageInfo_UpdateConfigParams proto.InternalMessageInfo
var xxx_messageInfo_ValidateConfigInput proto.InternalMessageInfo
func (m *UpdateConfigParams) GetConfigs() map[string]string {
func (m *ValidateConfigInput) GetConfigs() map[string]string {
if m != nil {
return m.Configs
}
return nil
}
type AddConfigInput struct {
Configs map[string]string `protobuf:"bytes,1,rep,name=configs,proto3" json:"configs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
DomainId string `protobuf:"bytes,2,opt,name=domainId,proto3" json:"domainId,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AddConfigInput) Reset() { *m = AddConfigInput{} }
func (m *AddConfigInput) String() string { return proto.CompactTextString(m) }
func (*AddConfigInput) ProtoMessage() {}
func (*AddConfigInput) Descriptor() ([]byte, []int) {
return fileDescriptor_63fdd68f7eb311f9, []int{2}
}
func (m *AddConfigInput) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AddConfigInput.Unmarshal(m, b)
}
func (m *AddConfigInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AddConfigInput.Marshal(b, m, deterministic)
}
func (m *AddConfigInput) XXX_Merge(src proto.Message) {
xxx_messageInfo_AddConfigInput.Merge(m, src)
}
func (m *AddConfigInput) XXX_Size() int {
return xxx_messageInfo_AddConfigInput.Size(m)
}
func (m *AddConfigInput) XXX_DiscardUnknown() {
xxx_messageInfo_AddConfigInput.DiscardUnknown(m)
}
var xxx_messageInfo_AddConfigInput proto.InternalMessageInfo
func (m *AddConfigInput) GetConfigs() map[string]string {
if m != nil {
return m.Configs
}
return nil
}
func (m *AddConfigInput) GetDomainId() string {
if m != nil {
return m.DomainId
}
return ""
}
type UpdateConfigInput struct {
Configs map[string]string `protobuf:"bytes,1,rep,name=configs,proto3" json:"configs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
DomainId string `protobuf:"bytes,2,opt,name=domainId,proto3" json:"domainId,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *UpdateConfigInput) Reset() { *m = UpdateConfigInput{} }
func (m *UpdateConfigInput) String() string { return proto.CompactTextString(m) }
func (*UpdateConfigInput) ProtoMessage() {}
func (*UpdateConfigInput) Descriptor() ([]byte, []int) {
return fileDescriptor_63fdd68f7eb311f9, []int{3}
}
func (m *UpdateConfigInput) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UpdateConfigInput.Unmarshal(m, b)
}
func (m *UpdateConfigInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_UpdateConfigInput.Marshal(b, m, deterministic)
}
func (m *UpdateConfigInput) XXX_Merge(src proto.Message) {
xxx_messageInfo_UpdateConfigInput.Merge(m, src)
}
func (m *UpdateConfigInput) XXX_Size() int {
return xxx_messageInfo_UpdateConfigInput.Size(m)
}
func (m *UpdateConfigInput) XXX_DiscardUnknown() {
xxx_messageInfo_UpdateConfigInput.DiscardUnknown(m)
}
var xxx_messageInfo_UpdateConfigInput proto.InternalMessageInfo
func (m *UpdateConfigInput) GetConfigs() map[string]string {
if m != nil {
return m.Configs
}
return nil
}
func (m *UpdateConfigInput) GetDomainId() string {
if m != nil {
return m.DomainId
}
return ""
}
type DeleteConfigInput struct {
DomainId string `protobuf:"bytes,1,opt,name=domainId,proto3" json:"domainId,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DeleteConfigInput) Reset() { *m = DeleteConfigInput{} }
func (m *DeleteConfigInput) String() string { return proto.CompactTextString(m) }
func (*DeleteConfigInput) ProtoMessage() {}
func (*DeleteConfigInput) Descriptor() ([]byte, []int) {
return fileDescriptor_63fdd68f7eb311f9, []int{4}
}
func (m *DeleteConfigInput) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeleteConfigInput.Unmarshal(m, b)
}
func (m *DeleteConfigInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DeleteConfigInput.Marshal(b, m, deterministic)
}
func (m *DeleteConfigInput) XXX_Merge(src proto.Message) {
xxx_messageInfo_DeleteConfigInput.Merge(m, src)
}
func (m *DeleteConfigInput) XXX_Size() int {
return xxx_messageInfo_DeleteConfigInput.Size(m)
}
func (m *DeleteConfigInput) XXX_DiscardUnknown() {
xxx_messageInfo_DeleteConfigInput.DiscardUnknown(m)
}
var xxx_messageInfo_DeleteConfigInput proto.InternalMessageInfo
func (m *DeleteConfigInput) GetDomainId() string {
if m != nil {
return m.DomainId
}
return ""
}
type CompleteConfigInput struct {
ConfigInput []*AddConfigInput `protobuf:"bytes,1,rep,name=ConfigInput,proto3" json:"ConfigInput,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CompleteConfigInput) Reset() { *m = CompleteConfigInput{} }
func (m *CompleteConfigInput) String() string { return proto.CompactTextString(m) }
func (*CompleteConfigInput) ProtoMessage() {}
func (*CompleteConfigInput) Descriptor() ([]byte, []int) {
return fileDescriptor_63fdd68f7eb311f9, []int{5}
}
func (m *CompleteConfigInput) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CompleteConfigInput.Unmarshal(m, b)
}
func (m *CompleteConfigInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CompleteConfigInput.Marshal(b, m, deterministic)
}
func (m *CompleteConfigInput) XXX_Merge(src proto.Message) {
xxx_messageInfo_CompleteConfigInput.Merge(m, src)
}
func (m *CompleteConfigInput) XXX_Size() int {
return xxx_messageInfo_CompleteConfigInput.Size(m)
}
func (m *CompleteConfigInput) XXX_DiscardUnknown() {
xxx_messageInfo_CompleteConfigInput.DiscardUnknown(m)
}
var xxx_messageInfo_CompleteConfigInput proto.InternalMessageInfo
func (m *CompleteConfigInput) GetConfigInput() []*AddConfigInput {
if m != nil {
return m.ConfigInput
}
return nil
}
type UseridByMobileParams struct {
Mobile string `protobuf:"bytes,1,opt,name=mobile,proto3" json:"mobile,omitempty"`
DomainId string `protobuf:"bytes,2,opt,name=domainId,proto3" json:"domainId,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@@ -154,7 +326,7 @@ func (m *UseridByMobileParams) Reset() { *m = UseridByMobileParams{} }
func (m *UseridByMobileParams) String() string { return proto.CompactTextString(m) }
func (*UseridByMobileParams) ProtoMessage() {}
func (*UseridByMobileParams) Descriptor() ([]byte, []int) {
return fileDescriptor_63fdd68f7eb311f9, []int{2}
return fileDescriptor_63fdd68f7eb311f9, []int{6}
}
func (m *UseridByMobileParams) XXX_Unmarshal(b []byte) error {
@@ -182,6 +354,13 @@ func (m *UseridByMobileParams) GetMobile() string {
return ""
}
func (m *UseridByMobileParams) GetDomainId() string {
if m != nil {
return m.DomainId
}
return ""
}
type Empty struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
@@ -192,7 +371,7 @@ func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
func (*Empty) Descriptor() ([]byte, []int) {
return fileDescriptor_63fdd68f7eb311f9, []int{3}
return fileDescriptor_63fdd68f7eb311f9, []int{7}
}
func (m *Empty) XXX_Unmarshal(b []byte) error {
@@ -224,7 +403,7 @@ func (m *UseridByMobileReply) Reset() { *m = UseridByMobileReply{} }
func (m *UseridByMobileReply) String() string { return proto.CompactTextString(m) }
func (*UseridByMobileReply) ProtoMessage() {}
func (*UseridByMobileReply) Descriptor() ([]byte, []int) {
return fileDescriptor_63fdd68f7eb311f9, []int{4}
return fileDescriptor_63fdd68f7eb311f9, []int{8}
}
func (m *UseridByMobileReply) XXX_Unmarshal(b []byte) error {
@@ -264,7 +443,7 @@ func (m *ValidateConfigReply) Reset() { *m = ValidateConfigReply{} }
func (m *ValidateConfigReply) String() string { return proto.CompactTextString(m) }
func (*ValidateConfigReply) ProtoMessage() {}
func (*ValidateConfigReply) Descriptor() ([]byte, []int) {
return fileDescriptor_63fdd68f7eb311f9, []int{5}
return fileDescriptor_63fdd68f7eb311f9, []int{9}
}
func (m *ValidateConfigReply) XXX_Unmarshal(b []byte) error {
@@ -299,22 +478,69 @@ 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"`
type SReceiver struct {
Contact string `protobuf:"bytes,1,opt,name=Contact,proto3" json:"Contact,omitempty"`
DomainId string `protobuf:"bytes,2,opt,name=DomainId,proto3" json:"DomainId,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SReceiver) Reset() { *m = SReceiver{} }
func (m *SReceiver) String() string { return proto.CompactTextString(m) }
func (*SReceiver) ProtoMessage() {}
func (*SReceiver) Descriptor() ([]byte, []int) {
return fileDescriptor_63fdd68f7eb311f9, []int{10}
}
func (m *SReceiver) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SReceiver.Unmarshal(m, b)
}
func (m *SReceiver) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SReceiver.Marshal(b, m, deterministic)
}
func (m *SReceiver) XXX_Merge(src proto.Message) {
xxx_messageInfo_SReceiver.Merge(m, src)
}
func (m *SReceiver) XXX_Size() int {
return xxx_messageInfo_SReceiver.Size(m)
}
func (m *SReceiver) XXX_DiscardUnknown() {
xxx_messageInfo_SReceiver.DiscardUnknown(m)
}
var xxx_messageInfo_SReceiver proto.InternalMessageInfo
func (m *SReceiver) GetContact() string {
if m != nil {
return m.Contact
}
return ""
}
func (m *SReceiver) GetDomainId() string {
if m != nil {
return m.DomainId
}
return ""
}
type BatchSendParams struct {
Receivers []*SReceiver `protobuf:"bytes,1,rep,name=Receivers,proto3" json:"Receivers,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}
return fileDescriptor_63fdd68f7eb311f9, []int{11}
}
func (m *BatchSendParams) XXX_Unmarshal(b []byte) error {
@@ -335,9 +561,9 @@ func (m *BatchSendParams) XXX_DiscardUnknown() {
var xxx_messageInfo_BatchSendParams proto.InternalMessageInfo
func (m *BatchSendParams) GetContacts() []string {
func (m *BatchSendParams) GetReceivers() []*SReceiver {
if m != nil {
return m.Contacts
return m.Receivers
}
return nil
}
@@ -371,18 +597,18 @@ func (m *BatchSendParams) GetRemoteTemplate() string {
}
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:"-"`
Receiver *SReceiver `protobuf:"bytes,1,opt,name=Receiver,proto3" json:"Receiver,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}
return fileDescriptor_63fdd68f7eb311f9, []int{12}
}
func (m *FailedRecord) XXX_Unmarshal(b []byte) error {
@@ -403,11 +629,11 @@ func (m *FailedRecord) XXX_DiscardUnknown() {
var xxx_messageInfo_FailedRecord proto.InternalMessageInfo
func (m *FailedRecord) GetContact() string {
func (m *FailedRecord) GetReceiver() *SReceiver {
if m != nil {
return m.Contact
return m.Receiver
}
return ""
return nil
}
func (m *FailedRecord) GetReason() string {
@@ -428,7 +654,7 @@ 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}
return fileDescriptor_63fdd68f7eb311f9, []int{13}
}
func (m *BatchSendReply) XXX_Unmarshal(b []byte) error {
@@ -456,55 +682,156 @@ func (m *BatchSendReply) GetFailedRecords() []*FailedRecord {
return nil
}
type ReadyInput struct {
DomainIds []string `protobuf:"bytes,1,rep,name=DomainIds,proto3" json:"DomainIds,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReadyInput) Reset() { *m = ReadyInput{} }
func (m *ReadyInput) String() string { return proto.CompactTextString(m) }
func (*ReadyInput) ProtoMessage() {}
func (*ReadyInput) Descriptor() ([]byte, []int) {
return fileDescriptor_63fdd68f7eb311f9, []int{14}
}
func (m *ReadyInput) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReadyInput.Unmarshal(m, b)
}
func (m *ReadyInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReadyInput.Marshal(b, m, deterministic)
}
func (m *ReadyInput) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReadyInput.Merge(m, src)
}
func (m *ReadyInput) XXX_Size() int {
return xxx_messageInfo_ReadyInput.Size(m)
}
func (m *ReadyInput) XXX_DiscardUnknown() {
xxx_messageInfo_ReadyInput.DiscardUnknown(m)
}
var xxx_messageInfo_ReadyInput proto.InternalMessageInfo
func (m *ReadyInput) GetDomainIds() []string {
if m != nil {
return m.DomainIds
}
return nil
}
type ReadyOutput struct {
Ok bool `protobuf:"varint,1,opt,name=Ok,proto3" json:"Ok,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReadyOutput) Reset() { *m = ReadyOutput{} }
func (m *ReadyOutput) String() string { return proto.CompactTextString(m) }
func (*ReadyOutput) ProtoMessage() {}
func (*ReadyOutput) Descriptor() ([]byte, []int) {
return fileDescriptor_63fdd68f7eb311f9, []int{15}
}
func (m *ReadyOutput) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReadyOutput.Unmarshal(m, b)
}
func (m *ReadyOutput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReadyOutput.Marshal(b, m, deterministic)
}
func (m *ReadyOutput) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReadyOutput.Merge(m, src)
}
func (m *ReadyOutput) XXX_Size() int {
return xxx_messageInfo_ReadyOutput.Size(m)
}
func (m *ReadyOutput) XXX_DiscardUnknown() {
xxx_messageInfo_ReadyOutput.DiscardUnknown(m)
}
var xxx_messageInfo_ReadyOutput proto.InternalMessageInfo
func (m *ReadyOutput) GetOk() bool {
if m != nil {
return m.Ok
}
return false
}
func init() {
proto.RegisterType((*SendParams)(nil), "apis.SendParams")
proto.RegisterType((*UpdateConfigParams)(nil), "apis.UpdateConfigParams")
proto.RegisterMapType((map[string]string)(nil), "apis.UpdateConfigParams.ConfigsEntry")
proto.RegisterType((*ValidateConfigInput)(nil), "apis.ValidateConfigInput")
proto.RegisterMapType((map[string]string)(nil), "apis.ValidateConfigInput.ConfigsEntry")
proto.RegisterType((*AddConfigInput)(nil), "apis.AddConfigInput")
proto.RegisterMapType((map[string]string)(nil), "apis.AddConfigInput.ConfigsEntry")
proto.RegisterType((*UpdateConfigInput)(nil), "apis.UpdateConfigInput")
proto.RegisterMapType((map[string]string)(nil), "apis.UpdateConfigInput.ConfigsEntry")
proto.RegisterType((*DeleteConfigInput)(nil), "apis.DeleteConfigInput")
proto.RegisterType((*CompleteConfigInput)(nil), "apis.CompleteConfigInput")
proto.RegisterType((*UseridByMobileParams)(nil), "apis.UseridByMobileParams")
proto.RegisterType((*Empty)(nil), "apis.Empty")
proto.RegisterType((*UseridByMobileReply)(nil), "apis.UseridByMobileReply")
proto.RegisterType((*ValidateConfigReply)(nil), "apis.ValidateConfigReply")
proto.RegisterType((*SReceiver)(nil), "apis.SReceiver")
proto.RegisterType((*BatchSendParams)(nil), "apis.BatchSendParams")
proto.RegisterType((*FailedRecord)(nil), "apis.FailedRecord")
proto.RegisterType((*BatchSendReply)(nil), "apis.BatchSendReply")
proto.RegisterType((*ReadyInput)(nil), "apis.ReadyInput")
proto.RegisterType((*ReadyOutput)(nil), "apis.ReadyOutput")
}
func init() { proto.RegisterFile("send_server.proto", fileDescriptor_63fdd68f7eb311f9) }
var fileDescriptor_63fdd68f7eb311f9 = []byte{
// 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,
// 722 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0x5d, 0x6b, 0xd4, 0x4c,
0x14, 0x26, 0xfb, 0xd9, 0x9c, 0xed, 0xbb, 0xed, 0x4e, 0xf7, 0xed, 0x9b, 0x77, 0x51, 0xa8, 0x41,
0x4b, 0x51, 0xbb, 0x42, 0x15, 0x59, 0x2a, 0x88, 0xdb, 0x2f, 0x69, 0xa1, 0xb4, 0x4c, 0x5b, 0x6f,
0x65, 0xba, 0x39, 0xae, 0xa1, 0xf9, 0x22, 0x99, 0x2d, 0xe4, 0xd2, 0xdf, 0xe0, 0x6f, 0xf0, 0xca,
0x5b, 0xff, 0x81, 0x7f, 0x4c, 0x32, 0x93, 0xc9, 0x26, 0xd9, 0x54, 0x11, 0xc1, 0xbb, 0x3c, 0x67,
0x9e, 0x73, 0xce, 0x93, 0x93, 0x39, 0x4f, 0xa0, 0x17, 0xa1, 0x67, 0xbd, 0x8f, 0x30, 0xbc, 0xc5,
0x70, 0x18, 0x84, 0x3e, 0xf7, 0x49, 0x83, 0x05, 0x76, 0x64, 0x7e, 0xd7, 0x00, 0x2e, 0xd0, 0xb3,
0xce, 0x59, 0xc8, 0xdc, 0x88, 0x3c, 0x81, 0x25, 0x8a, 0x13, 0xb4, 0x6f, 0x31, 0x34, 0xb4, 0x0d,
0x6d, 0xab, 0xb3, 0xb3, 0x32, 0x4c, 0x78, 0xc3, 0x0b, 0x15, 0xa6, 0x19, 0x81, 0xf4, 0xa1, 0x79,
0xe9, 0x07, 0xf6, 0xc4, 0xa8, 0x6d, 0x68, 0x5b, 0x3a, 0x95, 0x40, 0x44, 0x6d, 0xee, 0xa0, 0x51,
0x4f, 0xa3, 0x09, 0x20, 0x06, 0xb4, 0x4f, 0x31, 0x8a, 0xd8, 0x14, 0x8d, 0x86, 0x88, 0x2b, 0x48,
0x06, 0xb0, 0x74, 0x1e, 0xda, 0x7e, 0x68, 0xf3, 0xd8, 0x68, 0x8a, 0xa3, 0x0c, 0x93, 0x4d, 0xe8,
0x52, 0x74, 0x7d, 0x8e, 0x97, 0xe8, 0x06, 0x0e, 0xe3, 0x68, 0xb4, 0x04, 0xa3, 0x14, 0x35, 0x3f,
0x6b, 0xb0, 0xf6, 0x8e, 0x39, 0xb6, 0xc5, 0x38, 0xee, 0xfb, 0xde, 0x07, 0x7b, 0x7a, 0xec, 0x05,
0x33, 0x4e, 0xde, 0x40, 0x7b, 0x22, 0x60, 0x64, 0x68, 0x1b, 0xf5, 0xad, 0xce, 0xce, 0xa6, 0x7c,
0x9b, 0x0a, 0xee, 0x50, 0x3e, 0x47, 0x87, 0x1e, 0x0f, 0x63, 0xaa, 0xd2, 0x06, 0xbb, 0xb0, 0x9c,
0x3f, 0x20, 0xab, 0x50, 0xbf, 0xc1, 0x58, 0xcc, 0x46, 0xa7, 0xc9, 0x63, 0xf2, 0xbe, 0xb7, 0xcc,
0x99, 0xa1, 0x9a, 0x82, 0x00, 0xbb, 0xb5, 0x91, 0x66, 0x7e, 0xd1, 0xa0, 0x3b, 0xb6, 0xac, 0xbc,
0xa0, 0x57, 0x65, 0x41, 0x0f, 0xa4, 0xa0, 0x22, 0xad, 0x5a, 0x4b, 0x32, 0x29, 0xcb, 0x77, 0x99,
0xed, 0x1d, 0x5b, 0x69, 0xb3, 0x0c, 0xff, 0x91, 0xce, 0xaf, 0x1a, 0xf4, 0xae, 0x82, 0xf2, 0xec,
0x5e, 0x97, 0xa5, 0x3e, 0x94, 0x52, 0x17, 0x98, 0x7f, 0x59, 0xed, 0x33, 0xe8, 0x1d, 0xa0, 0x83,
0x45, 0xb1, 0xf9, 0x66, 0x5a, 0xb1, 0x99, 0x79, 0x0a, 0x6b, 0xfb, 0xbe, 0x1b, 0x94, 0x53, 0x5e,
0x42, 0x27, 0x07, 0xd3, 0x77, 0xec, 0x57, 0x7d, 0x0e, 0x9a, 0x27, 0x9a, 0x27, 0xd0, 0xbf, 0x8a,
0x30, 0xb4, 0xad, 0xbd, 0xf8, 0xd4, 0xbf, 0xb6, 0x1d, 0x4c, 0x57, 0x67, 0x1d, 0x5a, 0xae, 0xc0,
0xa9, 0x80, 0x14, 0xfd, 0x6c, 0x0e, 0x66, 0x1b, 0x9a, 0x87, 0x6e, 0xc0, 0x63, 0x73, 0x1b, 0xd6,
0x8a, 0x45, 0x29, 0x06, 0x4e, 0x9c, 0xd4, 0x9c, 0x89, 0xb0, 0xaa, 0x29, 0x91, 0x39, 0x2e, 0x5f,
0x77, 0x49, 0x37, 0xa0, 0x6d, 0x47, 0xe2, 0x40, 0xf0, 0x97, 0xa8, 0x82, 0xc9, 0x80, 0xdd, 0x68,
0x9a, 0xf6, 0x4f, 0x1e, 0xcd, 0x31, 0xe8, 0xd9, 0x4e, 0x27, 0x89, 0xfb, 0xbe, 0xc7, 0xd9, 0x84,
0xa7, 0x8d, 0x14, 0x4c, 0xd4, 0x1f, 0x94, 0xd4, 0x2b, 0x6c, 0x7e, 0xd3, 0x60, 0x65, 0x8f, 0xf1,
0xc9, 0xc7, 0x9c, 0x81, 0x6c, 0x83, 0xae, 0xaa, 0xaa, 0x7b, 0xb3, 0xe0, 0x20, 0x73, 0xc6, 0xdc,
0x2c, 0x6a, 0x77, 0x98, 0x45, 0xfd, 0x6e, 0xb3, 0x68, 0xfc, 0xd2, 0x2c, 0x9a, 0x95, 0x66, 0x71,
0x01, 0xcb, 0x47, 0xcc, 0x76, 0xd0, 0xa2, 0x38, 0xf1, 0x43, 0xeb, 0xf7, 0x3c, 0x6f, 0x1d, 0x5a,
0x14, 0x59, 0xe4, 0x7b, 0xa9, 0xe2, 0x14, 0x99, 0x27, 0xd0, 0xcd, 0x46, 0x21, 0x3f, 0xc6, 0x08,
0xfe, 0xc9, 0xb7, 0x51, 0xd3, 0x20, 0xb2, 0x76, 0xfe, 0x88, 0x16, 0x89, 0xe6, 0x63, 0x00, 0x8a,
0xcc, 0x8a, 0xe5, 0x3d, 0xbd, 0x07, 0xba, 0x9a, 0xb8, 0xac, 0xa1, 0xd3, 0x79, 0xc0, 0xbc, 0x0f,
0x1d, 0xc1, 0x3d, 0x9b, 0xf1, 0x84, 0xdc, 0x85, 0xda, 0xd9, 0x4d, 0xfa, 0xf1, 0x6b, 0x67, 0x37,
0x3b, 0x9f, 0x1a, 0xa0, 0x27, 0x92, 0xc6, 0x53, 0xf4, 0x38, 0x79, 0x0a, 0x4d, 0x41, 0x26, 0xab,
0x52, 0xc4, 0xbc, 0xcb, 0xa0, 0x97, 0x8b, 0xa4, 0xb5, 0x1e, 0x41, 0x23, 0x49, 0x55, 0xe4, 0xf9,
0x47, 0x1e, 0x74, 0x64, 0x44, 0x5c, 0x5d, 0x32, 0x04, 0x3d, 0x5b, 0x17, 0x52, 0xb9, 0x3f, 0x45,
0xfe, 0x08, 0xba, 0xc5, 0x75, 0x24, 0xff, 0xcb, 0xe3, 0x8a, 0x25, 0x2d, 0x66, 0xbe, 0x80, 0xe5,
0xbc, 0xf9, 0x90, 0xff, 0xee, 0x30, 0xa4, 0x85, 0xac, 0xbc, 0x5f, 0xa8, 0xac, 0x05, 0x0f, 0x29,
0x66, 0x1d, 0x41, 0xb7, 0xb8, 0x61, 0x4a, 0x65, 0xc5, 0xaf, 0x63, 0x50, 0x79, 0x24, 0x6f, 0xc1,
0x5b, 0xe8, 0x16, 0x17, 0x9b, 0x0c, 0x52, 0xd5, 0x15, 0x1e, 0xa2, 0x0a, 0x55, 0x59, 0xc1, 0x08,
0xf4, 0xec, 0x82, 0x91, 0x7f, 0x25, 0xaf, 0xb4, 0x7c, 0x83, 0x7e, 0x29, 0x2c, 0x32, 0xaf, 0x5b,
0xe2, 0x7f, 0xff, 0xfc, 0x47, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa6, 0x44, 0xad, 0xbd, 0x04, 0x08,
0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -519,9 +846,13 @@ const _ = grpc.SupportPackageIsVersion4
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type SendAgentClient interface {
Ready(ctx context.Context, in *ReadyInput, opts ...grpc.CallOption) (*ReadyOutput, error)
Send(ctx context.Context, in *SendParams, opts ...grpc.CallOption) (*Empty, error)
UpdateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*Empty, error)
ValidateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*ValidateConfigReply, error)
AddConfig(ctx context.Context, in *AddConfigInput, opts ...grpc.CallOption) (*Empty, error)
CompleteConfig(ctx context.Context, in *CompleteConfigInput, opts ...grpc.CallOption) (*Empty, error)
UpdateConfig(ctx context.Context, in *UpdateConfigInput, opts ...grpc.CallOption) (*Empty, error)
DeleteConfig(ctx context.Context, in *DeleteConfigInput, opts ...grpc.CallOption) (*Empty, error)
ValidateConfig(ctx context.Context, in *ValidateConfigInput, 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)
}
@@ -534,6 +865,15 @@ func NewSendAgentClient(cc *grpc.ClientConn) SendAgentClient {
return &sendAgentClient{cc}
}
func (c *sendAgentClient) Ready(ctx context.Context, in *ReadyInput, opts ...grpc.CallOption) (*ReadyOutput, error) {
out := new(ReadyOutput)
err := c.cc.Invoke(ctx, "/apis.SendAgent/Ready", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *sendAgentClient) Send(ctx context.Context, in *SendParams, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := c.cc.Invoke(ctx, "/apis.SendAgent/Send", in, out, opts...)
@@ -543,7 +883,25 @@ func (c *sendAgentClient) Send(ctx context.Context, in *SendParams, opts ...grpc
return out, nil
}
func (c *sendAgentClient) UpdateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*Empty, error) {
func (c *sendAgentClient) AddConfig(ctx context.Context, in *AddConfigInput, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := c.cc.Invoke(ctx, "/apis.SendAgent/AddConfig", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *sendAgentClient) CompleteConfig(ctx context.Context, in *CompleteConfigInput, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := c.cc.Invoke(ctx, "/apis.SendAgent/CompleteConfig", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *sendAgentClient) UpdateConfig(ctx context.Context, in *UpdateConfigInput, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := c.cc.Invoke(ctx, "/apis.SendAgent/UpdateConfig", in, out, opts...)
if err != nil {
@@ -552,7 +910,16 @@ func (c *sendAgentClient) UpdateConfig(ctx context.Context, in *UpdateConfigPara
return out, nil
}
func (c *sendAgentClient) ValidateConfig(ctx context.Context, in *UpdateConfigParams, opts ...grpc.CallOption) (*ValidateConfigReply, error) {
func (c *sendAgentClient) DeleteConfig(ctx context.Context, in *DeleteConfigInput, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := c.cc.Invoke(ctx, "/apis.SendAgent/DeleteConfig", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *sendAgentClient) ValidateConfig(ctx context.Context, in *ValidateConfigInput, opts ...grpc.CallOption) (*ValidateConfigReply, error) {
out := new(ValidateConfigReply)
err := c.cc.Invoke(ctx, "/apis.SendAgent/ValidateConfig", in, out, opts...)
if err != nil {
@@ -581,9 +948,13 @@ func (c *sendAgentClient) BatchSend(ctx context.Context, in *BatchSendParams, op
// SendAgentServer is the server API for SendAgent service.
type SendAgentServer interface {
Ready(context.Context, *ReadyInput) (*ReadyOutput, error)
Send(context.Context, *SendParams) (*Empty, error)
UpdateConfig(context.Context, *UpdateConfigParams) (*Empty, error)
ValidateConfig(context.Context, *UpdateConfigParams) (*ValidateConfigReply, error)
AddConfig(context.Context, *AddConfigInput) (*Empty, error)
CompleteConfig(context.Context, *CompleteConfigInput) (*Empty, error)
UpdateConfig(context.Context, *UpdateConfigInput) (*Empty, error)
DeleteConfig(context.Context, *DeleteConfigInput) (*Empty, error)
ValidateConfig(context.Context, *ValidateConfigInput) (*ValidateConfigReply, error)
UseridByMobile(context.Context, *UseridByMobileParams) (*UseridByMobileReply, error)
BatchSend(context.Context, *BatchSendParams) (*BatchSendReply, error)
}
@@ -592,13 +963,25 @@ type SendAgentServer interface {
type UnimplementedSendAgentServer struct {
}
func (*UnimplementedSendAgentServer) Ready(ctx context.Context, req *ReadyInput) (*ReadyOutput, error) {
return nil, status.Errorf(codes.Unimplemented, "method Ready not implemented")
}
func (*UnimplementedSendAgentServer) Send(ctx context.Context, req *SendParams) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method Send not implemented")
}
func (*UnimplementedSendAgentServer) UpdateConfig(ctx context.Context, req *UpdateConfigParams) (*Empty, error) {
func (*UnimplementedSendAgentServer) AddConfig(ctx context.Context, req *AddConfigInput) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method AddConfig not implemented")
}
func (*UnimplementedSendAgentServer) CompleteConfig(ctx context.Context, req *CompleteConfigInput) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method CompleteConfig not implemented")
}
func (*UnimplementedSendAgentServer) UpdateConfig(ctx context.Context, req *UpdateConfigInput) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateConfig not implemented")
}
func (*UnimplementedSendAgentServer) ValidateConfig(ctx context.Context, req *UpdateConfigParams) (*ValidateConfigReply, error) {
func (*UnimplementedSendAgentServer) DeleteConfig(ctx context.Context, req *DeleteConfigInput) (*Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteConfig not implemented")
}
func (*UnimplementedSendAgentServer) ValidateConfig(ctx context.Context, req *ValidateConfigInput) (*ValidateConfigReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method ValidateConfig not implemented")
}
func (*UnimplementedSendAgentServer) UseridByMobile(ctx context.Context, req *UseridByMobileParams) (*UseridByMobileReply, error) {
@@ -612,6 +995,24 @@ func RegisterSendAgentServer(s *grpc.Server, srv SendAgentServer) {
s.RegisterService(&_SendAgent_serviceDesc, srv)
}
func _SendAgent_Ready_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ReadyInput)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SendAgentServer).Ready(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/apis.SendAgent/Ready",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SendAgentServer).Ready(ctx, req.(*ReadyInput))
}
return interceptor(ctx, in, info, handler)
}
func _SendAgent_Send_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SendParams)
if err := dec(in); err != nil {
@@ -630,8 +1031,44 @@ func _SendAgent_Send_Handler(srv interface{}, ctx context.Context, dec func(inte
return interceptor(ctx, in, info, handler)
}
func _SendAgent_AddConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddConfigInput)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SendAgentServer).AddConfig(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/apis.SendAgent/AddConfig",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SendAgentServer).AddConfig(ctx, req.(*AddConfigInput))
}
return interceptor(ctx, in, info, handler)
}
func _SendAgent_CompleteConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CompleteConfigInput)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SendAgentServer).CompleteConfig(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/apis.SendAgent/CompleteConfig",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SendAgentServer).CompleteConfig(ctx, req.(*CompleteConfigInput))
}
return interceptor(ctx, in, info, handler)
}
func _SendAgent_UpdateConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateConfigParams)
in := new(UpdateConfigInput)
if err := dec(in); err != nil {
return nil, err
}
@@ -643,13 +1080,31 @@ func _SendAgent_UpdateConfig_Handler(srv interface{}, ctx context.Context, dec f
FullMethod: "/apis.SendAgent/UpdateConfig",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SendAgentServer).UpdateConfig(ctx, req.(*UpdateConfigParams))
return srv.(SendAgentServer).UpdateConfig(ctx, req.(*UpdateConfigInput))
}
return interceptor(ctx, in, info, handler)
}
func _SendAgent_DeleteConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteConfigInput)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SendAgentServer).DeleteConfig(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/apis.SendAgent/DeleteConfig",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SendAgentServer).DeleteConfig(ctx, req.(*DeleteConfigInput))
}
return interceptor(ctx, in, info, handler)
}
func _SendAgent_ValidateConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateConfigParams)
in := new(ValidateConfigInput)
if err := dec(in); err != nil {
return nil, err
}
@@ -661,7 +1116,7 @@ func _SendAgent_ValidateConfig_Handler(srv interface{}, ctx context.Context, dec
FullMethod: "/apis.SendAgent/ValidateConfig",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SendAgentServer).ValidateConfig(ctx, req.(*UpdateConfigParams))
return srv.(SendAgentServer).ValidateConfig(ctx, req.(*ValidateConfigInput))
}
return interceptor(ctx, in, info, handler)
}
@@ -706,14 +1161,30 @@ var _SendAgent_serviceDesc = grpc.ServiceDesc{
ServiceName: "apis.SendAgent",
HandlerType: (*SendAgentServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Ready",
Handler: _SendAgent_Ready_Handler,
},
{
MethodName: "Send",
Handler: _SendAgent_Send_Handler,
},
{
MethodName: "AddConfig",
Handler: _SendAgent_AddConfig_Handler,
},
{
MethodName: "CompleteConfig",
Handler: _SendAgent_CompleteConfig_Handler,
},
{
MethodName: "UpdateConfig",
Handler: _SendAgent_UpdateConfig_Handler,
},
{
MethodName: "DeleteConfig",
Handler: _SendAgent_DeleteConfig_Handler,
},
{
MethodName: "ValidateConfig",
Handler: _SendAgent_ValidateConfig_Handler,
+45 -8
View File
@@ -17,7 +17,7 @@ syntax = "proto3";
package apis;
message SendParams {
string Contact = 1;
SReceiver Receiver = 1;
string Topic = 2;
string Title = 3;
string Message = 4;
@@ -25,12 +25,31 @@ message SendParams {
string RemoteTemplate = 6;
}
message UpdateConfigParams {
message ValidateConfigInput {
map<string, string> configs = 1;
}
message AddConfigInput {
map<string, string> configs = 1;
string domainId = 2;
}
message UpdateConfigInput {
map<string, string> configs = 1;
string domainId = 2;
}
message DeleteConfigInput {
string domainId = 1;
}
message CompleteConfigInput {
repeated AddConfigInput ConfigInput = 1;
}
message UseridByMobileParams {
string mobile = 1;
string domainId = 2;
}
message Empty {
@@ -45,8 +64,13 @@ message ValidateConfigReply {
string msg = 2;
}
message SReceiver {
string Contact = 1;
string DomainId = 2;
}
message BatchSendParams {
repeated string Contacts = 1;
repeated SReceiver Receivers = 1;
string Title = 2;
string Message = 3;
string Priority = 4;
@@ -54,7 +78,7 @@ message BatchSendParams {
}
message FailedRecord {
string Contact = 1;
SReceiver Receiver = 1;
string Reason = 2;
}
@@ -62,10 +86,23 @@ message BatchSendReply {
repeated FailedRecord FailedRecords = 1;
}
message ReadyInput {
repeated string DomainIds = 1;
}
message ReadyOutput {
bool Ok = 1;
}
service SendAgent {
rpc Send(SendParams) returns (Empty);
rpc UpdateConfig(UpdateConfigParams) returns (Empty);
rpc ValidateConfig(UpdateConfigParams) returns (ValidateConfigReply);
rpc UseridByMobile(UseridByMobileParams) returns (UseridByMobileReply);
rpc Ready(ReadyInput) returns (ReadyOutput);
rpc Send (SendParams) returns (Empty);
rpc AddConfig (AddConfigInput) returns (Empty);
rpc CompleteConfig (CompleteConfigInput) returns (Empty);
rpc UpdateConfig (UpdateConfigInput) returns (Empty);
rpc DeleteConfig (DeleteConfigInput) returns (Empty);
rpc ValidateConfig (ValidateConfigInput) returns (ValidateConfigReply);
rpc UseridByMobile (UseridByMobileParams) returns (UseridByMobileReply);
rpc BatchSend (BatchSendParams) returns (BatchSendReply);
}
+199 -67
View File
@@ -34,7 +34,6 @@ import (
api "yunion.io/x/onecloud/pkg/apis/notify"
"yunion.io/x/onecloud/pkg/mcclient"
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"
)
@@ -135,11 +134,57 @@ func (self *SRpcService) BatchSend(ctx context.Context, contactType string, args
if len(args.RemoteTemplate) == 0 && contactType == api.MOBILE {
return nil, fmt.Errorf("empty remote template for mobile type notification")
}
domainIds := make([]string, len(args.Receivers))
for i := range domainIds {
domainIds[i] = args.Receivers[i].DomainId
}
checks, err := self.configStore.BatchCheckConfig(contactType, domainIds)
if err != nil {
return nil, errors.Wrap(err, "BatchCheckConfig")
}
hasSystem, err := self.configStore.HasSystemConfig(contactType)
if err != nil {
return nil, errors.Wrap(err, "HasSystemConfig")
}
ret := make([]*apis.FailedRecord, 0)
receiverIndex := 0
for i := range checks {
if checks[i] {
receiverIndex++
continue
}
if hasSystem {
args.Receivers[receiverIndex].DomainId = ""
receiverIndex++
continue
}
ret = append(ret, &apis.FailedRecord{
Receiver: args.Receivers[i],
Reason: fmt.Sprintf("no %q config for in domain %q and system", contactType, domainIds[i]),
})
args.Receivers = append(args.Receivers[:receiverIndex], args.Receivers[receiverIndex+1:]...)
}
f := func(service *apis.SendNotificationClient) (interface{}, error) {
// check ready
domainIds := make([]string, len(args.Receivers))
for i := range domainIds {
domainIds[i] = args.Receivers[i].DomainId
}
output, err := service.Ready(ctx, &apis.ReadyInput{DomainIds: domainIds})
if err != nil {
return nil, err
}
if !output.Ok {
// if NOINIT, try to restart server and send again
service, err = self.restartService(ctx, contactType)
if err != nil {
return nil, errors.Wrapf(err, "restart service %s failed", contactType)
}
}
return service.BatchSend(ctx, &args)
}
ret, err := self.execute(ctx, f, contactType)
i, err := self.execute(ctx, f, contactType)
if err != nil {
s, ok := status.FromError(err)
if !ok {
@@ -147,29 +192,54 @@ func (self *SRpcService) BatchSend(ctx context.Context, contactType string, args
}
return nil, errors.Error(s.Message())
}
reply := ret.(*apis.BatchSendReply)
return reply.FailedRecords, nil
reply := i.(*apis.BatchSendReply)
return append(ret, 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 notifyv2.SConfig, serviceName string) {
_, err := self.restartWithConfig(ctx, serviceName, config)
if err != nil {
log.Debugf("restart service failed: %s", err)
// UpdateConfig can update config for rpc service with domainId
func (self *SRpcService) UpdateConfig(ctx context.Context, service string, config notifyv2.SConfig) error {
var (
sendService *apis.SendNotificationClient
err error
)
sendService, ok := self.SendServices.Get(service)
if !ok {
return fmt.Errorf("no such service %s", service)
}
args := apis.UpdateConfigInput{
Configs: config.Config,
DomainId: config.DomainId,
}
_, err = sendService.UpdateConfig(ctx, &args)
if err != nil {
st := status.Convert(err)
if st.Code() != codes.NotFound {
return errors.Error(st.Message())
}
_, err = sendService.AddConfig(ctx, &apis.AddConfigInput{
Configs: config.Config,
DomainId: config.DomainId,
})
if err != nil {
return errors.Wrap(err, "try to add config but failed")
}
}
return nil
}
func (self *SRpcService) ContactByMobile(ctx context.Context, mobile, serviceName string) (string, error) {
func (self *SRpcService) ContactByMobile(ctx context.Context, mobile, serviceName string, domainId string) (string, error) {
iMobile := api.ParseInternationalMobile(mobile)
// compatible
if iMobile.AreaCode == "86" {
mobile = iMobile.Mobile
}
args := apis.UseridByMobileParams{}
args.Mobile = mobile
args := apis.UseridByMobileParams{
Mobile: mobile,
DomainId: domainId,
}
f := func(service *apis.SendNotificationClient) (interface{}, error) {
return service.UseridByMobile(ctx, &args)
@@ -246,40 +316,47 @@ func (self *SRpcService) execute(ctx context.Context, f func(client *apis.SendNo
return ret, nil
}
// restartSrevice fetch config from IServiceConfigStore and Call rpc.UpdateConfig
func (self *SRpcService) restartService(ctx context.Context, serviceName string) (*apis.SendNotificationClient, error) {
var ErrGetConfig = errors.Error("Get Config Failed")
config, err := self.configStore.GetConfig(serviceName)
func (self *SRpcService) completeConfig(ctx context.Context, serviceName string, sendService *apis.SendNotificationClient) error {
// get config
configs, err := self.configStore.GetConfigs(serviceName)
if err != nil {
log.Debugf("getConfig of serveice %s from database error", serviceName)
return nil, models.ErrGetConfig
log.Errorf("getConfig of serveice %s from database error", serviceName)
return ErrGetConfig
}
return self.restartWithConfig(ctx, serviceName, config)
// update config for service
configInput := make([]*apis.AddConfigInput, len(configs))
for i := range configInput {
configInput[i] = &apis.AddConfigInput{
Configs: configs[i].Config,
DomainId: configs[i].DomainId,
}
}
_, err = sendService.CompleteConfig(ctx, &apis.CompleteConfigInput{
ConfigInput: configInput,
})
if err != nil {
st := status.Convert(err)
if st.Code() == codes.FailedPrecondition {
// no such rpc serve
err = fmt.Errorf(st.Message())
}
if st.Code() == codes.Unavailable {
err = fmt.Errorf("service is unavailable for now: %s", st.Message())
}
return errors.Wrap(err, "UpdateConfig")
}
return nil
}
func (self *SRpcService) restartWithConfig(ctx context.Context, serviceName string,
config map[string]string) (*apis.SendNotificationClient, error) {
var (
sendService *apis.SendNotificationClient
err error
)
sendService, ok := self.SendServices.Get(serviceName)
func (self *SRpcService) restartService(ctx context.Context, service string) (*apis.SendNotificationClient, error) {
sendService, ok := self.SendServices.Get(service)
if !ok {
return nil, fmt.Errorf("no such service, please start new service")
}
args := apis.UpdateConfigParams{}
args.Configs = config
_, err = sendService.UpdateConfig(ctx, &args)
if err != nil {
st := status.Convert(err)
return nil, errors.Error(st.Message())
}
return sendService, nil
return sendService, self.completeConfig(ctx, service, sendService)
}
// startNewService try to start a new rpc service named serviceName
@@ -308,30 +385,7 @@ func (self *SRpcService) startNewService(ctx context.Context, serviceName string
return sendService, nil
}
// get config
config, err := self.configStore.GetConfig(serviceName)
if err != nil {
log.Errorf("getConfig of serveice %s from database error", serviceName)
return nil, models.ErrGetConfig
}
// update config for service
args := apis.UpdateConfigParams{}
args.Configs = config
_, err = sendService.UpdateConfig(ctx, &args)
if err != nil {
st := status.Convert(err)
if st.Code() == codes.FailedPrecondition {
// no such rpc serve
err = fmt.Errorf(st.Message())
}
if st.Code() == codes.Unavailable {
err = fmt.Errorf("service is unavailable for now: %s", st.Message())
}
return nil, errors.Wrap(err, "UpdateConfig")
}
return sendService, nil
return sendService, self.completeConfig(ctx, serviceName, sendService)
}
// closeService will remove service record from self.SendServices and try to remove sock file
@@ -374,8 +428,48 @@ func (self *SRpcService) updateService(ctx context.Context) error {
return nil
}
func (self *SRpcService) ValidateConfig(ctx context.Context, cType string, configs map[string]string) (isValid bool,
message string, err error) {
func (self *SRpcService) AddConfig(ctx context.Context, service string, config notifyv2.SConfig) error {
var (
sendService *apis.SendNotificationClient
err error
)
sendService, ok := self.SendServices.Get(service)
if !ok {
return fmt.Errorf("no such service %s", service)
}
args := apis.AddConfigInput{
DomainId: config.DomainId,
Configs: config.Config,
}
_, err = sendService.AddConfig(ctx, &args)
if err != nil {
return err
}
return nil
}
func (self *SRpcService) DeleteConfig(ctx context.Context, service, domainId string) error {
var (
sendService *apis.SendNotificationClient
err error
)
sendService, ok := self.SendServices.Get(service)
if !ok {
return fmt.Errorf("no such service %s", service)
}
args := apis.DeleteConfigInput{
DomainId: domainId,
}
_, err = sendService.DeleteConfig(ctx, &args)
if err != nil {
return err
}
return nil
}
func (self *SRpcService) ValidateConfig(ctx context.Context, cType string, configs map[string]string) (isValid bool, message string, err error) {
sendService, ok := self.SendServices.Get(cType)
@@ -389,7 +483,7 @@ func (self *SRpcService) ValidateConfig(ctx context.Context, cType string, confi
return
}
}
param := apis.UpdateConfigParams{
param := apis.ValidateConfigInput{
Configs: configs,
}
rep, err := sendService.ValidateConfig(ctx, &param)
@@ -405,6 +499,44 @@ func (self *SRpcService) ValidateConfig(ctx context.Context, cType string, confi
return rep.IsValid, rep.Msg, nil
}
func robotType2ContactType(rType string) string {
switch rType {
case api.ROBOT_TYPE_FEISHU:
return api.FEISHU_ROBOT
case api.ROBOT_TYPE_DINGTALK:
return api.DINGTALK_ROBOT
case api.ROBOT_TYPE_WORKWX:
return api.DINGTALK_ROBOT
case api.ROBOT_TYPE_WEBHOOK:
return api.WEBHOOK
}
return rType
}
func (self *SRpcService) SendRobotMessage(ctx context.Context, rType string, receivers []*apis.SReceiver, title string, message string) ([]*apis.FailedRecord, error) {
log.Infof("rType: %s", rType)
contactType := robotType2ContactType(rType)
args := apis.BatchSendParams{
Receivers: receivers,
Title: title,
Message: message,
}
f := func(service *apis.SendNotificationClient) (interface{}, error) {
return service.BatchSend(ctx, &args)
}
ret, err := self.execute(ctx, f, contactType)
if err != nil {
s, ok := status.FromError(err)
if !ok {
return nil, err
}
return nil, errors.Error(s.Message())
}
reply := ret.(*apis.BatchSendReply)
return reply.FailedRecords, nil
}
func grpcDialWithUnixSocket(ctx context.Context, socketPath string) (*grpc.ClientConn, error) {
return grpc.DialContext(ctx, socketPath, grpc.WithInsecure(), grpc.WithTimeout(time.Second*5), grpc.WithDialer(
func(addr string, timeout time.Duration) (net.Conn, error) {
+2
View File
@@ -37,6 +37,7 @@ func InitHandlers(app *appsrv.Application) {
db.TenantCacheManager,
db.RoleCacheManager,
models.SubContactManager,
db.SharedResourceManager,
models.VerificationManager,
models.SubscriptionReceiverManager,
} {
@@ -51,6 +52,7 @@ func InitHandlers(app *appsrv.Application) {
models.ConfigManager,
models.TemplateManager,
models.SubscriptionManager,
models.RobotManager,
} {
db.RegisterModelManager(manager)
handler := db.NewModelHandler(manager)
+141 -69
View File
@@ -37,6 +37,16 @@ func (self *NotificationSendTask) taskFailed(ctx context.Context, notification *
self.SetStageFailed(ctx, jsonutils.NewString(reason))
}
type DomainContact struct {
DomainId string
Contact string
}
type ReceiverSpec struct {
receiver models.IReceiver
rNotificaion *models.SReceiverNotification
}
func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
notification := obj.(*models.SNotification)
if notification.Status == apis.NOTIFICATION_STATUS_OK {
@@ -50,20 +60,6 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
}
notification.SetStatus(self.UserCred, apis.NOTIFICATION_STATUS_SENDING, "")
// split rns
var (
rnsWithReceiver []*models.SReceiverNotification
rnsWithoutReceiver []*models.SReceiverNotification
)
for i := range rns {
if rns[i].ReceiverID == models.ReceiverIdDefault {
rnsWithoutReceiver = append(rnsWithoutReceiver, &rns[i])
} else {
rnsWithReceiver = append(rnsWithReceiver, &rns[i])
}
}
failedRecord := make([]string, 0)
sendFail := func(rn *models.SReceiverNotification, reason string) {
rn.AfterSend(ctx, false, reason)
@@ -71,79 +67,80 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
}
// build contactMap
contactMap := make(map[string]*models.SReceiverNotification)
contactMapEn := make(map[string]*models.SReceiverNotification)
contactmapCn := make(map[string]*models.SReceiverNotification)
for i := range rnsWithReceiver {
if len(rnsWithReceiver[i].ReceiverID) == 0 {
contactMap[rnsWithReceiver[i].Contact] = rnsWithReceiver[i]
continue
}
receiver, err := rnsWithReceiver[i].Receiver()
receivers := make([]ReceiverSpec, 0, 0)
receiversEn := make([]ReceiverSpec, 0, len(rns)/2)
receiversCn := make([]ReceiverSpec, 0, len(rns)/2)
for i := range rns {
receiver, err := rns[i].Receiver()
if err != nil {
sendFail(rnsWithReceiver[i], fmt.Sprintf("fail to fetch Receiver: %s", err.Error()))
sendFail(&rns[i], fmt.Sprintf("fail to fetch Receiver: %s", err.Error()))
continue
}
// check receiver enabled
if receiver.Enabled.IsFalse() {
sendFail(rnsWithReceiver[i], fmt.Sprintf("disabled receiver"))
if !receiver.IsEnabled() {
sendFail(&rns[i], fmt.Sprintf("disabled receiver"))
continue
}
// check contact enabled
enabled, err := receiver.IsEnabledContactType(notification.ContactType)
if err != nil {
sendFail(rnsWithReceiver[i], fmt.Sprintf("IsEnabledContactType error for receiver: %s", err.Error()))
sendFail(&rns[i], fmt.Sprintf("IsEnabledContactType error for receiver: %s", err.Error()))
continue
}
if !enabled {
sendFail(rnsWithReceiver[i], fmt.Sprintf("disabled contactType %q", notification.ContactType))
sendFail(&rns[i], fmt.Sprintf("disabled contactType %q", notification.ContactType))
continue
}
// check contact verified
verified, err := receiver.IsVerifiedContactType(notification.ContactType)
if err != nil {
sendFail(rnsWithReceiver[i], fmt.Sprintf("IsVerifiedContactType error for receiver: %s", err.Error()))
sendFail(&rns[i], fmt.Sprintf("IsVerifiedContactType error for receiver: %s", err.Error()))
continue
}
if !verified {
sendFail(rnsWithReceiver[i], fmt.Sprintf("unverified contactType %q", notification.ContactType))
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(rnsWithReceiver[i], reason)
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
// }
lang, err := receiver.GetTemplateLang(ctx)
if err != nil {
reason := fmt.Sprintf("fail to GetTemplateLang: %s", err.Error())
sendFail(rnsWithReceiver[i], reason)
sendFail(&rns[i], reason)
continue
}
switch lang {
case "":
contactMap[contact] = rnsWithReceiver[i]
receivers = append(receivers, ReceiverSpec{
receiver: receiver,
rNotificaion: &rns[i],
})
case apis.TEMPLATE_LANG_EN:
contactMapEn[contact] = rnsWithReceiver[i]
receiversEn = append(receiversEn, ReceiverSpec{
receiver: receiver,
rNotificaion: &rns[i],
})
case apis.TEMPLATE_LANG_CN:
contactmapCn[contact] = rnsWithReceiver[i]
receiversCn = append(receiversCn, ReceiverSpec{
receiver: receiver,
rNotificaion: &rns[i],
})
}
}
for i := range rnsWithoutReceiver {
contactMap[rnsWithoutReceiver[i].Contact] = rnsWithoutReceiver[i]
}
var contactLen int
for lang, contactMap := range map[string]map[string]*models.SReceiverNotification{
"": contactMap,
apis.TEMPLATE_LANG_CN: contactmapCn,
apis.TEMPLATE_LANG_EN: contactMapEn,
for lang, receivers := range map[string][]ReceiverSpec{
"": receivers,
apis.TEMPLATE_LANG_CN: receiversCn,
apis.TEMPLATE_LANG_EN: receiversEn,
} {
if len(contactMap) == 0 {
if len(receivers) == 0 {
continue
}
@@ -154,40 +151,35 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
}
// 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)
for _, rn := range receivers {
rn.rNotificaion.BeforeSend(ctx, now)
}
contactLen += len(contacts)
contactLen += len(receivers)
// send
fds, err := models.NotifyService.BatchSend(ctx, notification.ContactType, rpcapi.BatchSendParams{
Contacts: contacts,
Title: p.Title,
Message: p.Message,
Priority: p.Priority,
RemoteTemplate: p.RemoteTemplate,
})
fds, err := self.batchSend(ctx, notification.ContactType, receivers, p)
if err != nil {
for _, rn := range contactMap {
sendFail(rn, err.Error())
for _, r := range receivers {
sendFail(r.rNotificaion, err.Error())
}
continue
}
// check result
failedRnIds := make(map[int64]struct{}, 0)
for _, fd := range fds {
rn := contactMap[fd.Contact]
sendFail(rn, fd.Reason)
delete(contactMap, fd.Contact)
sendFail(fd.rNotificaion, fd.Reason)
failedRnIds[fd.rNotificaion.RowId] = struct{}{}
}
// after send for successful notify
for _, rn := range contactMap {
rn.AfterSend(ctx, true, "")
for _, r := range receivers {
if _, ok := failedRnIds[r.rNotificaion.RowId]; ok {
continue
}
r.rNotificaion.AfterSend(ctx, true, "")
}
}
if len(failedRecord) > 0 && len(failedRecord) == contactLen {
if len(failedRecord) > 0 && len(failedRecord) >= len(rns) {
self.taskFailed(ctx, notification, strings.Join(failedRecord, "; "), true)
return
}
@@ -199,3 +191,83 @@ func (self *NotificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
logclient.AddActionLogWithContext(ctx, notification, logclient.ACT_SEND_NOTIFICATION, "", self.UserCred, true)
self.SetStageComplete(ctx, nil)
}
type FailedReceiverSpec struct {
ReceiverSpec
Reason string
}
func (self *NotificationSendTask) batchSend(ctx context.Context, contactType string, receivers []ReceiverSpec, params rpcapi.SendParams) (fails []FailedReceiverSpec, err error) {
log.Infof("contactType: %s, receivers: %s, params: %s", contactType, receivers, jsonutils.Marshal(params))
if contactType != apis.ROBOT {
return self._batchSend(ctx, contactType, receivers, func(res []*rpcapi.SReceiver) ([]*rpcapi.FailedRecord, error) {
return models.NotifyService.BatchSend(ctx, contactType, rpcapi.BatchSendParams{
Receivers: res,
Title: params.Title,
Message: params.Message,
Priority: params.Priority,
RemoteTemplate: params.RemoteTemplate,
})
})
}
robots := make(map[string][]ReceiverSpec)
for i := range receivers {
robot := receivers[i].receiver.(*models.SRobot)
robots[robot.Type] = append(robots[robot.Type], receivers[i])
}
for rType, robots := range robots {
_fails, err := self._batchSend(ctx, contactType, robots, func(res []*rpcapi.SReceiver) ([]*rpcapi.FailedRecord, error) {
return models.NotifyService.SendRobotMessage(ctx, rType, res, params.Title, params.Message)
})
if err != nil {
for i := range robots {
fails = append(fails, FailedReceiverSpec{
ReceiverSpec: robots[i],
Reason: err.Error(),
})
}
}
fails = append(fails, _fails...)
}
return fails, nil
}
func (self *NotificationSendTask) _batchSend(ctx context.Context, contactType string, receivers []ReceiverSpec, send func([]*rpcapi.SReceiver) ([]*rpcapi.FailedRecord, error)) (fails []FailedReceiverSpec, err error) {
rpcReceivers := make([]*rpcapi.SReceiver, len(receivers))
rpc2Receiver := make(map[DomainContact]ReceiverSpec, len(receivers))
for i := range receivers {
contact, err := receivers[i].receiver.GetContact(contactType)
if err != nil {
fails = append(fails, FailedReceiverSpec{
ReceiverSpec: receivers[i],
Reason: fmt.Sprintf("fail to fetch contact: %s", err.Error()),
})
continue
}
rpcReceivers[i] = &rpcapi.SReceiver{
DomainId: receivers[i].receiver.GetDomainId(),
Contact: contact,
}
rpc2Receiver[DomainContact{
DomainId: receivers[i].receiver.GetDomainId(),
Contact: contact,
}] = receivers[i]
}
fds, err := send(rpcReceivers)
if err != nil {
return nil, err
}
// check result
for _, fd := range fds {
dc := DomainContact{
DomainId: fd.Receiver.DomainId,
Contact: fd.Receiver.Contact,
}
receiver := rpc2Receiver[dc]
fails = append(fails, FailedReceiverSpec{
ReceiverSpec: receiver,
Reason: fd.Reason,
})
}
return
}
@@ -28,6 +28,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/db/lockman"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/notify/models"
"yunion.io/x/onecloud/pkg/apis/notify"
"yunion.io/x/onecloud/pkg/util/logclient"
)
@@ -61,6 +62,13 @@ func (self *RepullSuncontactTask) OnInit(ctx context.Context, obj db.IStandalone
}
subq := models.SubContactManager.Query("receiver_id").Equals("type", config.Type).SubQuery()
q := models.ReceiverManager.Query()
if config.Attribution == notify.CONFIG_ATTRIBUTION_DOMAIN {
q = q.Equals("domain_id", config.DomainId)
} else {
// The system-level config update should not affect the receiver under the domain with config
configq := models.ConfigManager.Query("domain_id").Equals("attribution", notify.CONFIG_ATTRIBUTION_DOMAIN).SubQuery()
q = q.Join(configq, sqlchemy.NotEquals(q.Field("domain_id"), configq.Field("domain_id")))
}
q.Join(subq, sqlchemy.Equals(q.Field("id"), subq.Field("receiver_id")))
rs := make([]models.SReceiver, 0)
err := db.FetchModelObjects(models.ReceiverManager, q, &rs)
+1 -1
View File
@@ -72,7 +72,7 @@ func (self *SubcontactPullTask) OnInit(ctx context.Context, obj db.IStandaloneMo
if !utils.IsInStringArray(cType, PullContactType) {
continue
}
userid, err := models.NotifyService.ContactByMobile(ctx, receiver.Mobile, cType)
userid, err := models.NotifyService.ContactByMobile(ctx, receiver.Mobile, cType, receiver.GetDomainId())
if err != nil {
var reason string
if errors.Cause(err) == notify.ErrNoSuchMobile {
+5 -1
View File
@@ -12,6 +12,7 @@ import (
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
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/logclient"
)
@@ -81,7 +82,10 @@ func (self *VerificationSendTask) OnInit(ctx context.Context, obj db.IStandalone
self.taskFailed(ctx, receiver, err.Error())
return
}
param.Contact = contact
param.Receiver = &apis.SReceiver{
Contact: contact,
DomainId: receiver.DomainId,
}
err = models.NotifyService.Send(ctx, contactType, param)
if err != nil {
self.taskFailed(ctx, receiver, err.Error())