diff --git a/cmd/climc/shell/notifyv2/config.go b/cmd/climc/shell/notifyv2/config.go index 0741883deb..46dec492ae 100644 --- a/cmd/climc/shell/notifyv2/config.go +++ b/cmd/climc/shell/notifyv2/config.go @@ -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)) } diff --git a/cmd/climc/shell/notifyv2/notification.go b/cmd/climc/shell/notifyv2/notification.go index 48b0868212..af8db36775 100644 --- a/cmd/climc/shell/notifyv2/notification.go +++ b/cmd/climc/shell/notifyv2/notification.go @@ -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, diff --git a/cmd/climc/shell/notifyv2/receiver.go b/cmd/climc/shell/notifyv2/receiver.go index ea69b9f454..76bc71a99e 100644 --- a/cmd/climc/shell/notifyv2/receiver.go +++ b/cmd/climc/shell/notifyv2/receiver.go @@ -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)) } diff --git a/cmd/climc/shell/notifyv2/robot.go b/cmd/climc/shell/notifyv2/robot.go new file mode 100644 index 0000000000..bbd7d8a63b --- /dev/null +++ b/cmd/climc/shell/notifyv2/robot.go @@ -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)) +} diff --git a/pkg/apis/notify/config.go b/pkg/apis/notify/config.go index c56f7b261c..258c6abd2c 100644 --- a/pkg/apis/notify/config.go +++ b/pkg/apis/notify/config.go @@ -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 { diff --git a/pkg/apis/notify/const.go b/pkg/apis/notify/const.go index 314306559c..89bdd3acc2 100644 --- a/pkg/apis/notify/const.go +++ b/pkg/apis/notify/const.go @@ -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" diff --git a/pkg/apis/notify/notification.go b/pkg/apis/notify/notification.go index 26b7ad778d..0abe056c1e 100644 --- a/pkg/apis/notify/notification.go +++ b/pkg/apis/notify/notification.go @@ -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 diff --git a/pkg/apis/notify/robot.go b/pkg/apis/notify/robot.go new file mode 100644 index 0000000000..2c989065f0 --- /dev/null +++ b/pkg/apis/notify/robot.go @@ -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"` +} diff --git a/pkg/cloudcommon/notifyclient/notify.go b/pkg/cloudcommon/notifyclient/notify.go index c2c5b8271e..2b975975a1 100644 --- a/pkg/cloudcommon/notifyclient/notify.go +++ b/pkg/cloudcommon/notifyclient/notify.go @@ -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) { diff --git a/pkg/cloudcommon/notifyclient/notify_internal.go b/pkg/cloudcommon/notifyclient/notify_internal.go index 9c93aee904..b83bea5b90 100644 --- a/pkg/cloudcommon/notifyclient/notify_internal.go +++ b/pkg/cloudcommon/notifyclient/notify_internal.go @@ -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 diff --git a/pkg/cloudcommon/notifyclient/template.go b/pkg/cloudcommon/notifyclient/template.go index 3148e1aec1..2cd3e2e549 100644 --- a/pkg/cloudcommon/notifyclient/template.go +++ b/pkg/cloudcommon/notifyclient/template.go @@ -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) diff --git a/pkg/mcclient/modules/mod_notify.go b/pkg/mcclient/modules/mod_notify.go index 67b363e7d8..75ccc1fd1b 100644 --- a/pkg/mcclient/modules/mod_notify.go +++ b/pkg/mcclient/modules/mod_notify.go @@ -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", diff --git a/pkg/mcclient/modules/notify/consts.go b/pkg/mcclient/modules/notify/consts.go index afa81e5803..b65d695814 100644 --- a/pkg/mcclient/modules/notify/consts.go +++ b/pkg/mcclient/modules/notify/consts.go @@ -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") diff --git a/pkg/mcclient/modules/notify/mod_notification.go b/pkg/mcclient/modules/notify/mod_notification.go index 4773a6c9cd..6fe8fbe6d0 100644 --- a/pkg/mcclient/modules/notify/mod_notification.go +++ b/pkg/mcclient/modules/notify/mod_notification.go @@ -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), diff --git a/pkg/mcclient/options/notify/config.go b/pkg/mcclient/options/notify/config.go new file mode 100644 index 0000000000..c9363c2559 --- /dev/null +++ b/pkg/mcclient/options/notify/config.go @@ -0,0 +1,82 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package notify + +import ( + "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 +} diff --git a/pkg/mcclient/options/notify/receiver.go b/pkg/mcclient/options/notify/receiver.go index 099f6e60bf..38f2e77cd2 100644 --- a/pkg/mcclient/options/notify/receiver.go +++ b/pkg/mcclient/options/notify/receiver.go @@ -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 +} diff --git a/pkg/mcclient/options/notify/robot.go b/pkg/mcclient/options/notify/robot.go new file mode 100644 index 0000000000..dac8ade0da --- /dev/null +++ b/pkg/mcclient/options/notify/robot.go @@ -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 +} diff --git a/pkg/notify/interface.go b/pkg/notify/interface.go index 014c80ed1b..fcc4a9d837 100644 --- a/pkg/notify/interface.go +++ b/pkg/notify/interface.go @@ -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") diff --git a/pkg/notify/models/config.go b/pkg/notify/models/config.go index 4306cab612..1c57783544 100644 --- a/pkg/notify/models/config.go +++ b/pkg/notify/models/config.go @@ -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) } diff --git a/pkg/notify/models/initdb.go b/pkg/notify/models/initdb.go index 2114489497..a5116a6f85 100644 --- a/pkg/notify/models/initdb.go +++ b/pkg/notify/models/initdb.go @@ -33,6 +33,7 @@ func InitDB() error { TemplateManager, ReceiverNotificationManager, SubscriptionManager, + RobotManager, } { err := manager.InitializeData() if err != nil { diff --git a/pkg/notify/models/notification.go b/pkg/notify/models/notification.go index 8e349b5040..a1ebc1d091 100644 --- a/pkg/notify/models/notification.go +++ b/pkg/notify/models/notification.go @@ -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") } diff --git a/pkg/notify/models/receiver.go b/pkg/notify/models/receiver.go index 79fe4b2c71..48dd796b5d 100644 --- a/pkg/notify/models/receiver.go +++ b/pkg/notify/models/receiver.go @@ -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 +} diff --git a/pkg/notify/models/receiver_notification.go b/pkg/notify/models/receiver_notification.go index ae2d525205..d04be9685b 100644 --- a/pkg/notify/models/receiver_notification.go +++ b/pkg/notify/models/receiver_notification.go @@ -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 +} diff --git a/pkg/notify/models/robot.go b/pkg/notify/models/robot.go new file mode 100644 index 0000000000..9049593c23 --- /dev/null +++ b/pkg/notify/models/robot.go @@ -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 +} diff --git a/pkg/notify/rpc/apis/send_client.go b/pkg/notify/rpc/apis/send_client.go index 64ad790004..3b9a913af8 100644 --- a/pkg/notify/rpc/apis/send_client.go +++ b/pkg/notify/rpc/apis/send_client.go @@ -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() diff --git a/pkg/notify/rpc/apis/send_server.pb.go b/pkg/notify/rpc/apis/send_server.pb.go index c3106b46b3..cb7e2c8626 100644 --- a/pkg/notify/rpc/apis/send_server.pb.go +++ b/pkg/notify/rpc/apis/send_server.pb.go @@ -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, diff --git a/pkg/notify/rpc/apis/send_server.proto b/pkg/notify/rpc/apis/send_server.proto index d5bc98880c..79d351182c 100644 --- a/pkg/notify/rpc/apis/send_server.proto +++ b/pkg/notify/rpc/apis/send_server.proto @@ -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 configs = 1; } +message AddConfigInput { + map configs = 1; + string domainId = 2; +} + +message UpdateConfigInput { + map 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); } + diff --git a/pkg/notify/rpc/send.go b/pkg/notify/rpc/send.go index 3bae183f22..271e48e64b 100644 --- a/pkg/notify/rpc/send.go +++ b/pkg/notify/rpc/send.go @@ -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, ¶m) @@ -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) { diff --git a/pkg/notify/service/handlers.go b/pkg/notify/service/handlers.go index 4d16e2331b..89d012ed45 100644 --- a/pkg/notify/service/handlers.go +++ b/pkg/notify/service/handlers.go @@ -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) diff --git a/pkg/notify/tasks/notifications_send_task.go b/pkg/notify/tasks/notifications_send_task.go index 2b90193839..add8bfbc1a 100644 --- a/pkg/notify/tasks/notifications_send_task.go +++ b/pkg/notify/tasks/notifications_send_task.go @@ -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 +} diff --git a/pkg/notify/tasks/repull_subcontact_task.go b/pkg/notify/tasks/repull_subcontact_task.go index 7e5cc11543..961e896534 100644 --- a/pkg/notify/tasks/repull_subcontact_task.go +++ b/pkg/notify/tasks/repull_subcontact_task.go @@ -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) diff --git a/pkg/notify/tasks/subcontact_pull_task.go b/pkg/notify/tasks/subcontact_pull_task.go index a6af78382a..8b503fe1b1 100644 --- a/pkg/notify/tasks/subcontact_pull_task.go +++ b/pkg/notify/tasks/subcontact_pull_task.go @@ -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 { diff --git a/pkg/notify/tasks/verification_send_task.go b/pkg/notify/tasks/verification_send_task.go index 8784af67ce..6cb53ee31e 100644 --- a/pkg/notify/tasks/verification_send_task.go +++ b/pkg/notify/tasks/verification_send_task.go @@ -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())