mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-10-28 03:23:25 +08:00
feat(notify): add subscription in notify
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
// 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"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options/notify"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cmd := shell.NewResourceCmd(&modules.NotifySubscription).WithKeyword("notify-subscription")
|
||||
cmd.List(new(notify.SubscriptionListOptions))
|
||||
cmd.Show(new(notify.SubscriptionOptions))
|
||||
cmd.Perform("set-receiver", new(notify.SubscriptionSetReceiverOptions))
|
||||
cmd.Perform("set-robot", new(notify.SubscriptionSetRobotOptions))
|
||||
cmd.Perform("set-webhook", new(notify.SubscriptionSetWebhookOptions))
|
||||
}
|
||||
@@ -69,4 +69,32 @@ const (
|
||||
|
||||
CTYPE_ROBOT_YES = "yes"
|
||||
CTYPE_ROBOT_ONLY = "only"
|
||||
|
||||
SUBSCRIPTION_RESOURCE_CREATE_DELETE = "resource create delete"
|
||||
SUBSCRIPTION_RESOURCE_CHANGECONFIG = "resource change config"
|
||||
SUBSCRIPTION_RESOURCE_UPDATE = "resource udpate"
|
||||
SUBSCRIPTION_RESOURCE_EXPIRED_RELEASE = "resource expired release"
|
||||
SUBSCRIPTION_AUTOMATED_PROCESS_EXECUTION = "automated process execution"
|
||||
|
||||
SUBSCRIPTION_TYPE_RESOURCE = "resource"
|
||||
SUBSCRIPTION_TYPE_AUTOMATED_PROCESS = "automated_process"
|
||||
|
||||
SUBSCRIPTION_RESOURCE_SERVER = "server"
|
||||
SUBSCRIPTION_RESOURCE_SCALINGGROUP = "scalinggroup"
|
||||
SUBSCRIPTION_RESOURCE_SCALINGPOLICY = "scalingpolicy"
|
||||
SUBSCRIPTION_RESOURCE_IMAGE = "image"
|
||||
SUBSCRIPTION_RESOURCE_DISK = "disk"
|
||||
SUBSCRIPTION_RESOURCE_SNAPSHOT = "snapshot"
|
||||
SUBSCRIPTION_RESOURCE_INSTANCESNAPSHOT = "instance_snapshot"
|
||||
SUBSCRIPTION_RESOURCE_SNAPSHOTPOLICY = "snapshotpolicy"
|
||||
SUBSCRIPTION_RESOURCE_NETWORK = "network"
|
||||
SUBSCRIPTION_RESOURCE_EIP = "eip"
|
||||
SUBSCRIPTION_RESOURCE_SECGROUP = "secgroup"
|
||||
SUBSCRIPTION_RESOURCE_LOADBALANCER = "loadbalancer"
|
||||
SUBSCRIPTION_RESOURCE_LOADBALANCERACL = "loadbalanceracl"
|
||||
SUBSCRIPTION_RESOURCE_LOADBALANCERCERTIFICATE = "loadbalancercertificate"
|
||||
SUBSCRIPTION_RESOURCE_BUCKET = "bucket"
|
||||
SUBSCRIPTION_RESOURCE_DBINSTANCE = "dbinstance"
|
||||
SUBSCRIPTION_RESOURCE_ELASTICCACHE = "elasticcache"
|
||||
SUBSCRIPTION_RESOURCE_SCHEDULEDTASK = "scheduledtask"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
Event SEvent
|
||||
|
||||
ActionCreate SAction = "create"
|
||||
ActionDelete SAction = "delete"
|
||||
ActionPendingDelete SAction = "pending_delete"
|
||||
ActionUpdate SAction = "update"
|
||||
ActionRebuildRoot SAction = "rebuild_root"
|
||||
ActionResetPassword SAction = "reset_password"
|
||||
ActionChangeConfig SAction = "change_config"
|
||||
ActionResize SAction = "resize"
|
||||
ActionExpiredRelease SAction = "expired_release"
|
||||
ActionExecute SAction = "execute"
|
||||
)
|
||||
|
||||
const (
|
||||
DelimiterInEvent = "/"
|
||||
)
|
||||
|
||||
type SAction string
|
||||
|
||||
type SEvent struct {
|
||||
resourceType string
|
||||
action SAction
|
||||
}
|
||||
|
||||
func (se SEvent) WithResourceType(rt string) SEvent {
|
||||
se.resourceType = rt
|
||||
return se
|
||||
}
|
||||
|
||||
func (se SEvent) WithAction(a SAction) SEvent {
|
||||
se.action = a
|
||||
return se
|
||||
}
|
||||
|
||||
func (se SEvent) ResourceType() string {
|
||||
return se.resourceType
|
||||
}
|
||||
|
||||
func (se SEvent) Action() SAction {
|
||||
return se.action
|
||||
}
|
||||
|
||||
func (se SEvent) String() string {
|
||||
return strings.ToUpper(fmt.Sprintf("%s%s%s", se.ResourceType(), DelimiterInEvent, se.Action()))
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package notify
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/apis"
|
||||
|
||||
type SubscriptionListInput struct {
|
||||
}
|
||||
|
||||
type SubscriptionDetails struct {
|
||||
apis.StatusStandaloneResourceDetails
|
||||
// description: resources managed
|
||||
// example: ["server", "eip", "disk"]
|
||||
Resources []string `json:"resource_types"`
|
||||
// description: receivers of the message sent
|
||||
Receivers SubscriptionReceiver `json:"receivers"`
|
||||
// description: type of robot send message
|
||||
// example: dingtalk_robot
|
||||
Robot string `json:"robot"`
|
||||
// description: webhook send message
|
||||
// example: webhook
|
||||
Webhook string `json:"webhook"`
|
||||
}
|
||||
|
||||
type IDAndName struct {
|
||||
// example: 036fed49483b412888a760c2bc995caa
|
||||
ID string `json:"id"`
|
||||
// example: test
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type ReceivingRoleIDAndName struct {
|
||||
IDAndName
|
||||
// description: scope of role
|
||||
// enum: system,domain,project
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
type SubscriptionReceiver struct {
|
||||
Receivers []IDAndName `json:"receivers"`
|
||||
ReceivingRoles []ReceivingRoleIDAndName `json:"roles"`
|
||||
}
|
||||
|
||||
type SubscriptionSetReceiverInput struct {
|
||||
ReceivingRoles []ReceivingRole `json:"roles"`
|
||||
Receivers []string `json:"receivers"`
|
||||
}
|
||||
|
||||
type ReceivingRole struct {
|
||||
// description: id or name of role
|
||||
Role string `json:"role"`
|
||||
// description: scope of role
|
||||
// enum: system,domain,project
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
type SubscriptionSetRobotInput struct {
|
||||
// description: robot
|
||||
// enum: feishu-robot,dingtalk-robot,workwx-robot
|
||||
Robot string
|
||||
}
|
||||
|
||||
type SubscriptionSetWebhookInput struct {
|
||||
Webhook string
|
||||
}
|
||||
@@ -21,11 +21,12 @@ type ConfigsManager struct {
|
||||
}
|
||||
|
||||
var (
|
||||
NotifyReceiver modulebase.ResourceManager
|
||||
NotifyConfig modulebase.ResourceManager
|
||||
Notification modulebase.ResourceManager
|
||||
NotifyTemplate modulebase.ResourceManager
|
||||
Configs ConfigsManager
|
||||
NotifyReceiver modulebase.ResourceManager
|
||||
NotifyConfig modulebase.ResourceManager
|
||||
Notification modulebase.ResourceManager
|
||||
NotifyTemplate modulebase.ResourceManager
|
||||
NotifySubscription modulebase.ResourceManager
|
||||
Configs ConfigsManager
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -60,4 +61,12 @@ func init() {
|
||||
[]string{},
|
||||
)
|
||||
register(&NotifyTemplate)
|
||||
|
||||
NotifySubscription = NewNotifyv2Manager(
|
||||
"subscription",
|
||||
"subscriptions",
|
||||
[]string{"ID", "Name", "Type", "Resource_Types", "Receivers", "Robot", "Webhook"},
|
||||
[]string{},
|
||||
)
|
||||
register(&NotifySubscription)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient/options"
|
||||
)
|
||||
|
||||
type SubscriptionListOptions struct {
|
||||
options.BaseListOptions
|
||||
}
|
||||
|
||||
func (opts *SubscriptionListOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return options.ListStructToParams(opts)
|
||||
}
|
||||
|
||||
type SubscriptionOptions struct {
|
||||
ID string `help:"Id or Name of subscription"`
|
||||
}
|
||||
|
||||
func (so *SubscriptionOptions) GetId() string {
|
||||
return so.ID
|
||||
}
|
||||
|
||||
func (so *SubscriptionOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type SubscriptionSetReceiverOptions struct {
|
||||
SubscriptionOptions
|
||||
Receivers []string `json:"receivers"`
|
||||
RoleAndScopes []string `json:"role_and_scopes" help:"role and scope, separated by a colon, example: admin:system"`
|
||||
}
|
||||
|
||||
func (opts *SubscriptionSetReceiverOptions) Params() (jsonutils.JSONObject, error) {
|
||||
d := jsonutils.NewDict()
|
||||
if len(opts.Receivers) > 0 {
|
||||
d.Set("receivers", jsonutils.NewStringArray(opts.Receivers))
|
||||
}
|
||||
if len(opts.RoleAndScopes) > 0 {
|
||||
ra := jsonutils.NewArray()
|
||||
for _, rs := range opts.RoleAndScopes {
|
||||
index := strings.Index(rs, ":")
|
||||
if index <= 0 {
|
||||
return nil, fmt.Errorf("invalid role_and_scope %q, example: %s", rs, "admin:system")
|
||||
}
|
||||
rd := jsonutils.NewDict()
|
||||
rd.Set("role", jsonutils.NewString(rs[:index]))
|
||||
rd.Set("scope", jsonutils.NewString(rs[index+1:]))
|
||||
ra.Add(rd)
|
||||
}
|
||||
d.Set("roles", ra)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
type SsubscriptionSetRobotOptions struct {
|
||||
ROBOT string `choices:"feishu-robot|dingtalk-robot|workwx-robot"`
|
||||
}
|
||||
|
||||
type SubscriptionSetRobotOptions struct {
|
||||
SubscriptionOptions
|
||||
SsubscriptionSetRobotOptions
|
||||
}
|
||||
|
||||
func (opts *SubscriptionSetRobotOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(opts.SsubscriptionSetRobotOptions), nil
|
||||
}
|
||||
|
||||
type SsubscriptionSetWebhookOptions struct {
|
||||
WEBHOOK string `choices:"webhook"`
|
||||
}
|
||||
|
||||
type SubscriptionSetWebhookOptions struct {
|
||||
SubscriptionOptions
|
||||
SsubscriptionSetWebhookOptions
|
||||
}
|
||||
|
||||
func (opts *SubscriptionSetWebhookOptions) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(opts.SsubscriptionSetWebhookOptions), nil
|
||||
}
|
||||
@@ -988,6 +988,7 @@ func (rm *SReceiverManager) OnAdd(obj *jsonutils.JSONDict) {
|
||||
|
||||
func (rm *SReceiverManager) OnUpdate(oldObj, newObj *jsonutils.JSONDict) {
|
||||
userId, _ := newObj.GetString("id")
|
||||
log.Infof("receiver delete event for user %q", userId)
|
||||
receivers, err := rm.FetchByIDs(context.Background(), userId)
|
||||
if err != nil {
|
||||
log.Errorf("fail to FetchByIDs: %v", err)
|
||||
@@ -1016,6 +1017,7 @@ func (rm *SReceiverManager) OnUpdate(oldObj, newObj *jsonutils.JSONDict) {
|
||||
|
||||
func (rm *SReceiverManager) OnDelete(obj *jsonutils.JSONDict) {
|
||||
userId, _ := obj.GetString("id")
|
||||
log.Infof("receiver delete event for user %q", userId)
|
||||
receivers, err := rm.FetchByIDs(context.Background(), userId)
|
||||
if err != nil {
|
||||
log.Errorf("fail to FetchByIDs: %v", err)
|
||||
|
||||
@@ -0,0 +1,575 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
"yunion.io/x/pkg/utils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/bitmap"
|
||||
"yunion.io/x/onecloud/pkg/util/rbacutils"
|
||||
"yunion.io/x/onecloud/pkg/util/stringutils2"
|
||||
)
|
||||
|
||||
func parseEvent(es string) (notify.SEvent, error) {
|
||||
ess := strings.Split(es, notify.DelimiterInEvent)
|
||||
if len(ess) != 2 {
|
||||
return notify.SEvent{}, fmt.Errorf("invalid event string %q", es)
|
||||
}
|
||||
return notify.Event.WithResourceType(ess[0]).WithAction(notify.SAction(ess[1])), nil
|
||||
}
|
||||
|
||||
type SSubscriptionManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
db.SEnabledResourceBaseManager
|
||||
}
|
||||
|
||||
var SubscriptionManager *SSubscriptionManager
|
||||
|
||||
func init() {
|
||||
SubscriptionManager = &SSubscriptionManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SSubscription{},
|
||||
"subscription_tbl",
|
||||
"subscription",
|
||||
"subscriptions",
|
||||
),
|
||||
}
|
||||
SubscriptionManager.SetVirtualObject(SubscriptionManager)
|
||||
}
|
||||
|
||||
type SSubscription struct {
|
||||
db.SStandaloneResourceBase
|
||||
db.SEnabledResourceBase
|
||||
|
||||
Type string `width:"20" nullable:"false" create:"required" update:"user" list:"user"`
|
||||
Resources uint64 `nullable:"false"`
|
||||
Actions uint32 `nullable:"false"`
|
||||
AdvanceDays int `nullable:"false"`
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultResourceCreateDelete = "resource create or delete"
|
||||
DefaultResourceChangeConfig = "resource change config"
|
||||
DefaultResourceUpdate = "resource update"
|
||||
DefaultResourceReleaseDue1Day = "resource release due 1 day"
|
||||
DefaultResourceReleaseDue3Day = "resource release due 3 day"
|
||||
DefaultScheduledTaskExecute = "scheduled task execute"
|
||||
DefaultScalingPolicyExecute = "scaling policy execute"
|
||||
DefaultSnapshotPolicyExecute = "snapshot policy execute"
|
||||
)
|
||||
|
||||
func (sm *SSubscriptionManager) InitializeData() error {
|
||||
initSNames := sets.NewString(
|
||||
DefaultResourceCreateDelete,
|
||||
DefaultResourceChangeConfig,
|
||||
DefaultResourceUpdate,
|
||||
DefaultResourceReleaseDue1Day,
|
||||
DefaultResourceReleaseDue3Day,
|
||||
DefaultScheduledTaskExecute,
|
||||
DefaultScalingPolicyExecute,
|
||||
DefaultSnapshotPolicyExecute,
|
||||
)
|
||||
q := sm.Query()
|
||||
subscriptions := make([]SSubscription, 0, initSNames.Len())
|
||||
err := db.FetchModelObjects(sm, q, &subscriptions)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to FetchModelObjects")
|
||||
}
|
||||
for i := range subscriptions {
|
||||
ss := &subscriptions[i]
|
||||
initSNames.Delete(ss.Name)
|
||||
}
|
||||
ctx := context.Background()
|
||||
for _, name := range initSNames.UnsortedList() {
|
||||
ss := new(SSubscription)
|
||||
ss.Name = name
|
||||
switch name {
|
||||
case DefaultResourceCreateDelete:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_SCALINGGROUP,
|
||||
notify.SUBSCRIPTION_RESOURCE_IMAGE,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_SNAPSHOT,
|
||||
notify.SUBSCRIPTION_RESOURCE_INSTANCESNAPSHOT,
|
||||
notify.SUBSCRIPTION_RESOURCE_SNAPSHOTPOLICY,
|
||||
notify.SUBSCRIPTION_RESOURCE_NETWORK,
|
||||
notify.SUBSCRIPTION_RESOURCE_EIP,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCERACL,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCERCERTIFICATE,
|
||||
notify.SUBSCRIPTION_RESOURCE_BUCKET,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(
|
||||
notify.ActionCreate,
|
||||
notify.ActionDelete,
|
||||
notify.ActionPendingDelete,
|
||||
)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
case DefaultResourceChangeConfig:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(notify.ActionChangeConfig)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
case DefaultResourceUpdate:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(notify.ActionUpdate)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
case DefaultResourceReleaseDue1Day:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_EIP,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(notify.ActionExpiredRelease)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
ss.AdvanceDays = 1
|
||||
case DefaultResourceReleaseDue3Day:
|
||||
ss.addResources(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_EIP,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
)
|
||||
ss.addAction(notify.ActionExpiredRelease)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_RESOURCE
|
||||
ss.AdvanceDays = 3
|
||||
case DefaultScheduledTaskExecute:
|
||||
ss.addResources(notify.SUBSCRIPTION_RESOURCE_SCHEDULEDTASK)
|
||||
ss.addAction(notify.ActionExecute)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_AUTOMATED_PROCESS
|
||||
case DefaultScalingPolicyExecute:
|
||||
ss.addResources(notify.SUBSCRIPTION_RESOURCE_SCALINGPOLICY)
|
||||
ss.addAction(notify.ActionExecute)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_AUTOMATED_PROCESS
|
||||
case DefaultSnapshotPolicyExecute:
|
||||
ss.addResources(notify.SUBSCRIPTION_RESOURCE_SNAPSHOTPOLICY)
|
||||
ss.addAction(notify.ActionExecute)
|
||||
ss.Type = notify.SUBSCRIPTION_TYPE_AUTOMATED_PROCESS
|
||||
}
|
||||
err := sm.TableSpec().Insert(ctx, ss)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to insert %s", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sm *SSubscriptionManager) ListItemFilter(ctx context.Context, q *sqlchemy.SQuery, userCred mcclient.TokenCredential, input notify.SubscriptionListInput) (*sqlchemy.SQuery, error) {
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func (sm *SSubscriptionManager) FetchCustomizeColumns(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, objs []interface{}, fields stringutils2.SSortedStrings, isList bool) []notify.SubscriptionDetails {
|
||||
sRows := sm.SStandaloneResourceBaseManager.FetchCustomizeColumns(ctx, userCred, query, objs, fields, isList)
|
||||
rows := make([]notify.SubscriptionDetails, len(objs))
|
||||
for i := range rows {
|
||||
rows[i].StandaloneResourceDetails = sRows[i]
|
||||
ss := objs[i].(*SSubscription)
|
||||
rows[i].Resources = ss.getResources()
|
||||
srs, err := ss.subscriptionReceiverDiss()
|
||||
if err != nil {
|
||||
log.Errorf("unable to get subscriptionReceivers: %v", err)
|
||||
}
|
||||
for j := range srs {
|
||||
sr := &srs[j]
|
||||
switch sr.ReceiverType {
|
||||
case ReceiverNormal:
|
||||
rows[i].Receivers.Receivers = append(rows[i].Receivers.Receivers, notify.IDAndName{
|
||||
ID: sr.Receiver,
|
||||
Name: sr.ReceiverName,
|
||||
})
|
||||
case ReceiverRole:
|
||||
rows[i].Receivers.ReceivingRoles = append(rows[i].Receivers.ReceivingRoles, notify.ReceivingRoleIDAndName{
|
||||
IDAndName: notify.IDAndName{
|
||||
ID: sr.Receiver,
|
||||
Name: sr.RoleName,
|
||||
},
|
||||
Scope: sr.RoleScope,
|
||||
})
|
||||
case ReceiverDingtalkRobot, ReceiverFeishuRobot, ReceiverWorkwxRobot:
|
||||
rows[i].Robot = sr.ReceiverType
|
||||
case ReceiverWeebhook:
|
||||
rows[i].Webhook = sr.ReceiverType
|
||||
}
|
||||
}
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
type SSubscriptionReceiverDis struct {
|
||||
SSubscriptionReceiver
|
||||
ReceiverName string `json:"receiver_name"`
|
||||
RoleName string `json:"role_name"`
|
||||
}
|
||||
|
||||
func (s *SSubscription) subscriptionReceiverDiss() ([]SSubscriptionReceiverDis, error) {
|
||||
q := SubscriptionReceiverManager.Query().Equals("subscription_id", s.Id)
|
||||
rq := ReceiverManager.Query("id", "name").SubQuery()
|
||||
roq := db.RoleCacheManager.Query("id", "name").SubQuery()
|
||||
q = q.LeftJoin(rq, sqlchemy.Equals(q.Field("receiver"), rq.Field("id")))
|
||||
q = q.LeftJoin(roq, sqlchemy.Equals(q.Field("receiver"), roq.Field("id")))
|
||||
// It looks strange, but the order of append cannot be changed
|
||||
q.AppendField(q.QueryFields()...)
|
||||
q.AppendField(rq.Field("name", "receiver_name"))
|
||||
q.AppendField(roq.Field("name", "role_name"))
|
||||
srs := make([]SSubscriptionReceiverDis, 0)
|
||||
err := q.All(&srs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to fetch All")
|
||||
}
|
||||
return srs, nil
|
||||
}
|
||||
|
||||
func (sm *SSubscriptionManager) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCredential, ownerId mcclient.IIdentityProvider, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
return nil, httperrors.NewForbiddenError("prohibit creation")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
return input, httperrors.NewForbiddenError("update prohibited")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) ValidateDeleteCondition(ctx context.Context) error {
|
||||
return httperrors.NewForbiddenError("prohibit deletion")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) AllowPerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, ss, "set-receiver")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) PerformSetReceiver(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.SubscriptionSetReceiverInput) (jsonutils.JSONObject, error) {
|
||||
// check input
|
||||
errgo, _ := errgroup.WithContext(ctx)
|
||||
// check receiving roles
|
||||
validScopes := []string{string(rbacutils.ScopeSystem), string(rbacutils.ScopeDomain), string(rbacutils.ScopeProject)}
|
||||
for i := range input.ReceivingRoles {
|
||||
role := input.ReceivingRoles[i]
|
||||
index := i
|
||||
errgo.Go(func() error {
|
||||
if len(role.Scope) == 0 {
|
||||
return httperrors.NewInputParameterError("empty scope for role %q", role.Role)
|
||||
}
|
||||
if !utils.IsInStringArray(role.Scope, validScopes) {
|
||||
return httperrors.NewInputParameterError("invalid scope %q for role %q, need %s, %s or %s", role.Scope, role.Role, rbacutils.ScopeSystem, rbacutils.ScopeDomain, rbacutils.ScopeProject)
|
||||
}
|
||||
roleCache, err := db.RoleCacheManager.FetchRoleByIdOrName(ctx, role.Role)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable find role %q", role)
|
||||
}
|
||||
input.ReceivingRoles[index].Role = roleCache.GetId()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
err := errgo.Wait()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
receivers, err := ReceiverManager.FetchByIdOrNames(ctx, input.Receivers...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to fetch Receivers")
|
||||
}
|
||||
reSet := sets.NewString(input.Receivers...)
|
||||
reIds := make([]string, len(receivers))
|
||||
for i := range receivers {
|
||||
reSet.Delete(receivers[i].GetId())
|
||||
reSet.Delete(receivers[i].GetName())
|
||||
reIds[i] = receivers[i].GetId()
|
||||
}
|
||||
if reSet.Len() > 0 {
|
||||
return nil, httperrors.NewInputParameterError("receivers %q not found", strings.Join(reSet.UnsortedList(), ", "))
|
||||
}
|
||||
input.Receivers = reIds
|
||||
|
||||
// deal with subscriptionReceivers
|
||||
srs, err := SubscriptionReceiverManager.findReceivers(ss.Id, ReceiverNormal, ReceiverRole)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to findReceivers")
|
||||
}
|
||||
reSet = sets.NewString(input.Receivers...)
|
||||
reRoleSet := make(map[notify.ReceivingRole]struct{})
|
||||
for _, role := range input.ReceivingRoles {
|
||||
reRoleSet[role] = struct{}{}
|
||||
}
|
||||
for i := range srs {
|
||||
rs := &srs[i]
|
||||
switch rs.ReceiverType {
|
||||
case ReceiverNormal:
|
||||
if !reSet.Has(rs.Receiver) {
|
||||
err := rs.Delete(ctx, userCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to delete receiver %s", rs.Receiver)
|
||||
}
|
||||
}
|
||||
reSet.Delete(rs.Receiver)
|
||||
case ReceiverRole:
|
||||
role := rs.receivingRole()
|
||||
if _, ok := reRoleSet[role]; !ok {
|
||||
err := rs.Delete(ctx, userCred)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to delete receiver %s", rs.Receiver)
|
||||
}
|
||||
}
|
||||
delete(reRoleSet, role)
|
||||
}
|
||||
}
|
||||
for _, re := range reSet.UnsortedList() {
|
||||
_, err := SubscriptionReceiverManager.create(ctx, ss.Id, re, ReceiverNormal, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to create receiver %s", re)
|
||||
}
|
||||
}
|
||||
for role := range reRoleSet {
|
||||
_, err := SubscriptionReceiverManager.create(ctx, ss.Id, role.Role, ReceiverRole, role.Scope)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to create role %s", role)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ss *SSubscription) AllowPerformSetRobot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, ss, "set-robot")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) PerformSetRobot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.SubscriptionSetRobotInput) (jsonutils.JSONObject, error) {
|
||||
err := ss.setSingleReceiver(ctx, input.Robot, ReceiverDingtalkRobot, ReceiverFeishuRobot, ReceiverWorkwxRobot)
|
||||
if errors.Cause(err) == errors.ErrNotFound {
|
||||
return nil, httperrors.NewInputParameterError("unkown robot %q", input.Robot)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (ss *SSubscription) AllowPerformSetWebhook(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowPerform(userCred, ss, "set-webhook")
|
||||
}
|
||||
|
||||
func (ss *SSubscription) PerformSetWebhook(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input notify.SubscriptionSetWebhookInput) (jsonutils.JSONObject, error) {
|
||||
err := ss.setSingleReceiver(ctx, input.Webhook, ReceiverWeebhook)
|
||||
if errors.Cause(err) == errors.ErrNotFound {
|
||||
return nil, httperrors.NewInputParameterError("unkown webhook %q", input.Webhook)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (ss *SSubscription) setSingleReceiver(ctx context.Context, re string, reTypes ...string) error {
|
||||
receiverRobot := reTypes
|
||||
if !utils.IsInStringArray(re, receiverRobot) {
|
||||
return errors.ErrNotFound
|
||||
}
|
||||
srs, err := SubscriptionReceiverManager.findReceivers(ss.Id, receiverRobot...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to findReceivers")
|
||||
}
|
||||
if len(srs) == 0 {
|
||||
// create one
|
||||
_, err := SubscriptionReceiverManager.create(ctx, ss.Id, "", re, "")
|
||||
return err
|
||||
}
|
||||
if len(srs) > 1 {
|
||||
return fmt.Errorf("multi robot receiver")
|
||||
}
|
||||
// update
|
||||
sr := &srs[0]
|
||||
_, err = db.Update(sr, func() error {
|
||||
sr.ReceiverType = re
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SSubscription) addResources(resources ...string) {
|
||||
for _, resource := range resources {
|
||||
v := converter.resourceValue(resource)
|
||||
if v < 0 {
|
||||
continue
|
||||
}
|
||||
s.Resources += 1 << v
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SSubscription) addAction(actions ...notify.SAction) {
|
||||
for _, action := range actions {
|
||||
v := converter.actionValue(action)
|
||||
if v < 0 {
|
||||
continue
|
||||
}
|
||||
s.Actions += 1 << v
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SSubscription) getResources() []string {
|
||||
vs := bitmap.Uint64ToIntArray(s.Resources)
|
||||
resources := make([]string, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
resources = append(resources, converter.resource(v))
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
func (s *SSubscription) getActions() []notify.SAction {
|
||||
vs := bitmap.Uint2IntArray(s.Actions)
|
||||
actions := make([]notify.SAction, 0, len(vs))
|
||||
for _, v := range vs {
|
||||
actions = append(actions, converter.action(v))
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
func (sm *SSubscriptionManager) SubsciptionByEvent(eventStr string, advanceDays int) ([]SSubscription, error) {
|
||||
event, err := parseEvent(eventStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to parse event %q", event)
|
||||
}
|
||||
resourceV := converter.resourceValue(event.ResourceType())
|
||||
actionV := converter.actionValue(event.Action())
|
||||
q := sm.Query().Equals("advance_days", advanceDays)
|
||||
q = q.Filter(sqlchemy.GT(sqlchemy.AND_Val("", q.Field("resources"), 1<<resourceV), 0))
|
||||
q = q.Filter(sqlchemy.GT(sqlchemy.AND_Val("", q.Field("actions"), 1<<actionV), 0))
|
||||
var subscriptions []SSubscription
|
||||
err = db.FetchModelObjects(sm, q, &subscriptions)
|
||||
if err != nil {
|
||||
q.DebugQuery()
|
||||
return nil, errors.Wrap(err, "unable to FetchModelObjects")
|
||||
}
|
||||
return subscriptions, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
converter = &sConverter{
|
||||
resourceValueMap: make(map[string]int, 5),
|
||||
resourceList: make([]string, 0, 5),
|
||||
actionList: make([]notify.SAction, 0, 5),
|
||||
actionValueMap: make(map[notify.SAction]int, 5),
|
||||
}
|
||||
converter.registerResource(
|
||||
notify.SUBSCRIPTION_RESOURCE_SERVER,
|
||||
notify.SUBSCRIPTION_RESOURCE_SCALINGGROUP,
|
||||
notify.SUBSCRIPTION_RESOURCE_SCALINGPOLICY,
|
||||
notify.SUBSCRIPTION_RESOURCE_IMAGE,
|
||||
notify.SUBSCRIPTION_RESOURCE_DISK,
|
||||
notify.SUBSCRIPTION_RESOURCE_SNAPSHOT,
|
||||
notify.SUBSCRIPTION_RESOURCE_INSTANCESNAPSHOT,
|
||||
notify.SUBSCRIPTION_RESOURCE_SNAPSHOTPOLICY,
|
||||
notify.SUBSCRIPTION_RESOURCE_NETWORK,
|
||||
notify.SUBSCRIPTION_RESOURCE_EIP,
|
||||
notify.SUBSCRIPTION_RESOURCE_SECGROUP,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCER,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCERACL,
|
||||
notify.SUBSCRIPTION_RESOURCE_LOADBALANCERCERTIFICATE,
|
||||
notify.SUBSCRIPTION_RESOURCE_BUCKET,
|
||||
notify.SUBSCRIPTION_RESOURCE_DBINSTANCE,
|
||||
notify.SUBSCRIPTION_RESOURCE_ELASTICCACHE,
|
||||
notify.SUBSCRIPTION_RESOURCE_SCHEDULEDTASK,
|
||||
)
|
||||
converter.registerAction(
|
||||
notify.ActionCreate,
|
||||
notify.ActionDelete,
|
||||
notify.ActionPendingDelete,
|
||||
notify.ActionUpdate,
|
||||
notify.ActionChangeConfig,
|
||||
notify.ActionExpiredRelease,
|
||||
notify.ActionExecute,
|
||||
)
|
||||
}
|
||||
|
||||
var converter *sConverter
|
||||
|
||||
type sConverter struct {
|
||||
resourceValueMap map[string]int
|
||||
resourceList []string
|
||||
actionValueMap map[notify.SAction]int
|
||||
actionList []notify.SAction
|
||||
}
|
||||
|
||||
func (rc *sConverter) registerResource(resources ...string) {
|
||||
for _, resource := range resources {
|
||||
if _, ok := rc.resourceValueMap[resource]; ok {
|
||||
return
|
||||
}
|
||||
rc.resourceList = append(rc.resourceList, resource)
|
||||
rc.resourceValueMap[resource] = len(rc.resourceList) - 1
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *sConverter) registerAction(actions ...notify.SAction) {
|
||||
for _, action := range actions {
|
||||
if _, ok := rc.actionValueMap[action]; ok {
|
||||
return
|
||||
}
|
||||
rc.actionList = append(rc.actionList, action)
|
||||
rc.actionValueMap[action] = len(rc.actionList) - 1
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *sConverter) resourceValue(resource string) int {
|
||||
v, ok := rc.resourceValueMap[resource]
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (rc *sConverter) resource(resourceValue int) string {
|
||||
if resourceValue < 0 || resourceValue >= len(rc.resourceList) {
|
||||
return ""
|
||||
}
|
||||
return rc.resourceList[resourceValue]
|
||||
}
|
||||
|
||||
func (rc *sConverter) actionValue(action notify.SAction) int {
|
||||
v, ok := rc.actionValueMap[action]
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (rc *sConverter) action(actionValue int) notify.SAction {
|
||||
if actionValue < 0 || actionValue >= len(rc.actionList) {
|
||||
return notify.SAction("")
|
||||
}
|
||||
return rc.actionList[actionValue]
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package models
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/notify"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
)
|
||||
|
||||
var SubscriptionReceiverManager *SSubscriptionReceiverManager
|
||||
|
||||
func init() {
|
||||
SubscriptionReceiverManager = &SSubscriptionReceiverManager{
|
||||
SStandaloneResourceBaseManager: db.NewStandaloneResourceBaseManager(
|
||||
SSubscriptionReceiver{},
|
||||
"subscriptionreceiver_tbl",
|
||||
"subscriptionreceiver",
|
||||
"subscriptionreceivers",
|
||||
),
|
||||
}
|
||||
SubscriptionReceiverManager.SetVirtualObject(ReceiverNotificationManager)
|
||||
}
|
||||
|
||||
type SSubscriptionReceiverManager struct {
|
||||
db.SStandaloneResourceBaseManager
|
||||
}
|
||||
|
||||
type SSubscriptionReceiver struct {
|
||||
db.SStandaloneResourceBase
|
||||
|
||||
SubscriptionID string `width:"128" charset:"ascii" nullable:"fase" index:"true"`
|
||||
// role id or receiver id or other and the value type is determined by the ReceiverType
|
||||
Receiver string `width:"128" charset:"ascii" nullable:"false"`
|
||||
ReceiverType string `width:"16" charset:"ascii" nullable:"false" index:"true"`
|
||||
RoleScope string `width:"8" charset:"ascii" nullable:"false"`
|
||||
}
|
||||
|
||||
const (
|
||||
ReceiverRole = "role"
|
||||
ReceiverNormal = "normal"
|
||||
ReceiverFeishuRobot = notify.FEISHU_ROBOT
|
||||
ReceiverDingtalkRobot = notify.DINGTALK_ROBOT
|
||||
ReceiverWorkwxRobot = notify.WORKWX_ROBOT
|
||||
ReceiverWeebhook = "webhook"
|
||||
|
||||
ScopeSystem = "system"
|
||||
ScopeDomain = "domain"
|
||||
ScopeProject = "project"
|
||||
)
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) robot(ssid string) (string, error) {
|
||||
return srm.findSingleReceiver(ReceiverFeishuRobot, ReceiverDingtalkRobot, ReceiverWorkwxRobot)
|
||||
}
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) webhook(ssid string) (string, error) {
|
||||
return srm.findSingleReceiver(ssid, ReceiverWeebhook)
|
||||
}
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) findSingleReceiver(ssid string, receiverTypes ...string) (string, error) {
|
||||
srs, err := srm.findReceivers(ssid, receiverTypes...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(srs) > 1 {
|
||||
return "", errors.Error("multi receiver")
|
||||
}
|
||||
if len(srs) == 0 {
|
||||
return "", errors.ErrNotFound
|
||||
}
|
||||
return srs[0].ReceiverType, nil
|
||||
}
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) findReceivers(ssid string, receiverTypes ...string) ([]SSubscriptionReceiver, error) {
|
||||
if len(receiverTypes) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
q := srm.Query().Equals("subscription_id", ssid)
|
||||
if len(receiverTypes) == 1 {
|
||||
q = q.Equals("receiver_type", receiverTypes[0])
|
||||
} else {
|
||||
q = q.In("receiver_type", receiverTypes)
|
||||
}
|
||||
srs := make([]SSubscriptionReceiver, 0)
|
||||
err := db.FetchModelObjects(srm, q, &srs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to FetchModelObjects")
|
||||
}
|
||||
return srs, nil
|
||||
}
|
||||
|
||||
// TODO: Use cache to increase speed
|
||||
func (srm *SSubscriptionReceiverManager) getReceivers(ctx context.Context, ssid string, projectDomainId string, projectId string) ([]string, error) {
|
||||
srs, err := srm.findReceivers(ssid, ReceiverRole, ReceiverNormal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
receivers := make([]string, 0, len(srs))
|
||||
roleMap := make(map[string][]string, 3)
|
||||
receivermap := make(map[string]*[]string, 3)
|
||||
for _, sr := range srs {
|
||||
if sr.ReceiverType == ReceiverNormal {
|
||||
receivers = append(receivers, sr.Receiver)
|
||||
} else if sr.ReceiverType == ReceiverRole {
|
||||
roleMap[sr.RoleScope] = append(roleMap[sr.RoleScope], sr.Receiver)
|
||||
receivermap[sr.RoleScope] = &[]string{}
|
||||
}
|
||||
}
|
||||
errgo, _ := errgroup.WithContext(ctx)
|
||||
for scope, roles := range roleMap {
|
||||
receivers := receivermap[scope]
|
||||
errgo.Go(func() error {
|
||||
query := jsonutils.NewDict()
|
||||
query.Set("roles", jsonutils.NewStringArray(roles))
|
||||
query.Set("effective", jsonutils.JSONTrue)
|
||||
switch scope {
|
||||
case ScopeSystem:
|
||||
case ScopeDomain:
|
||||
if len(projectDomainId) == 0 {
|
||||
return fmt.Errorf("need projectDomainId")
|
||||
}
|
||||
query.Set("project_domain_id", jsonutils.NewString(projectDomainId))
|
||||
case ScopeProject:
|
||||
if len(projectId) == 0 {
|
||||
return fmt.Errorf("need projectId")
|
||||
}
|
||||
query.Add(jsonutils.NewString(projectId), "scope", "project", "id")
|
||||
}
|
||||
s := auth.GetAdminSession(ctx, "", "")
|
||||
log.Debugf("query for role-assignments: %s", query.String())
|
||||
listRet, err := modules.RoleAssignments.List(s, query)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to list RoleAssignments")
|
||||
}
|
||||
log.Debugf("return value for role-assignments: %s", jsonutils.Marshal(listRet))
|
||||
for i := range listRet.Data {
|
||||
ras := listRet.Data[i]
|
||||
user, err := ras.Get("user")
|
||||
if err == nil {
|
||||
id, err := user.GetString("id")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to get user.id from result of RoleAssignments.List")
|
||||
}
|
||||
*receivers = append(*receivers, id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
err = errgo.Wait()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, res := range receivermap {
|
||||
receivers = append(receivers, *res...)
|
||||
}
|
||||
return receivers, nil
|
||||
}
|
||||
|
||||
func (srm *SSubscriptionReceiverManager) create(ctx context.Context, ssid, receiver, receiverType, roleScope string) (*SSubscriptionReceiver, error) {
|
||||
sr := &SSubscriptionReceiver{
|
||||
SubscriptionID: ssid,
|
||||
Receiver: receiver,
|
||||
ReceiverType: receiverType,
|
||||
RoleScope: roleScope,
|
||||
}
|
||||
err := srm.TableSpec().Insert(ctx, sr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to insert")
|
||||
}
|
||||
return sr, nil
|
||||
}
|
||||
|
||||
func (sr *SSubscriptionReceiver) receivingRole() notify.ReceivingRole {
|
||||
return notify.ReceivingRole{
|
||||
Role: sr.Receiver,
|
||||
Scope: sr.RoleScope,
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,10 @@ func InitHandlers(app *appsrv.Application) {
|
||||
|
||||
db.UserCacheManager,
|
||||
db.TenantCacheManager,
|
||||
db.RoleCacheManager,
|
||||
models.SubContactManager,
|
||||
models.VerificationManager,
|
||||
models.SubscriptionReceiverManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
}
|
||||
@@ -48,6 +50,7 @@ func InitHandlers(app *appsrv.Application) {
|
||||
models.NotificationManager,
|
||||
models.ConfigManager,
|
||||
models.TemplateManager,
|
||||
models.SubscriptionManager,
|
||||
} {
|
||||
db.RegisterModelManager(manager)
|
||||
handler := db.NewModelHandler(manager)
|
||||
|
||||
Reference in New Issue
Block a user