Skip to content

Commit

Permalink
Merge pull request #3451 from AllenShen/feature/collaboration_group
Browse files Browse the repository at this point in the history
add user group support for collaboration and release plan approve
  • Loading branch information
jamsman94 authored Apr 24, 2024
2 parents 5d88df7 + 8ea38fd commit ba7c492
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ func (c *CollaborationModeColl) Update(username string, args *models.Collaborati
"update_time": time.Now().Unix(),
"update_by": username,
"members": args.Members,
"member_info": args.MemberInfo,
"workflows": args.Workflows,
"recycle_day": args.RecycleDay,
"revision": res.Revision + 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
config2 "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/label/config"
workflowservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/workflow/service/workflow"
"github.com/koderover/zadig/v2/pkg/setting"
"github.com/koderover/zadig/v2/pkg/shared/client/user"
"github.com/koderover/zadig/v2/pkg/tool/log"
)

Expand Down Expand Up @@ -296,9 +297,20 @@ func updateVisitTime(uid string, cis []*models.CollaborationInstance, logger *za
}

func GetCollaborationUpdate(projectName, uid, identityType, userName string, logger *zap.SugaredLogger) (*GetCollaborationUpdateResp, error) {
relatedGroups, err := user.New().GetUserGroupsByUid(uid)
if err != nil {
logger.Errorf("GetCollaborationUpdate error, err msg:%s", err)
return nil, err
}
members := []string{uid}
for _, group := range relatedGroups.GroupList {
members = append(members, group.ID)
}

// user uid and related gids to get collaboration mode
collaborations, err := mongodb.NewCollaborationModeColl().List(&mongodb.CollaborationModeListOptions{
Projects: []string{projectName},
Members: []string{uid},
Members: members,
})
if err != nil {
logger.Errorf("GetCollaborationUpdate error, err msg:%s", err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,32 @@ limitations under the License.
package service

import (
"fmt"

"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/util/sets"

"github.com/koderover/zadig/v2/pkg/microservice/aslan/core/collaboration/repository/models"
"github.com/koderover/zadig/v2/pkg/microservice/aslan/core/collaboration/repository/mongodb"
)

func validateMemberInfo(collaborationMode *models.CollaborationMode) bool {
if len(collaborationMode.Members) != len(collaborationMode.MemberInfo) {
return false
}
memberSet := sets.NewString(collaborationMode.Members...)
memberInfoSet := sets.NewString()
for _, memberInfo := range collaborationMode.MemberInfo {
memberInfoSet.Insert(memberInfo.GetID())
}
return memberSet.Equal(memberInfoSet)
}

func CreateCollaborationMode(userName string, collaborationMode *models.CollaborationMode, logger *zap.SugaredLogger) error {
if !validateMemberInfo(collaborationMode) {
return fmt.Errorf("members and member_info not match")
}
err := mongodb.NewCollaborationModeColl().Create(userName, collaborationMode)
if err != nil {
logger.Errorf("CreateCollaborationMode error, err msg:%s", err)
Expand All @@ -34,6 +52,9 @@ func CreateCollaborationMode(userName string, collaborationMode *models.Collabor
}

func UpdateCollaborationMode(userName string, collaborationMode *models.CollaborationMode, logger *zap.SugaredLogger) error {
if !validateMemberInfo(collaborationMode) {
return fmt.Errorf("members and member_info not match")
}
err := mongodb.NewCollaborationModeColl().Update(userName, collaborationMode)
if err != nil {
logger.Errorf("UpdateCollaborationMode error, err msg:%s", err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,11 @@ type Approval struct {
}

type NativeApproval struct {
Timeout int `bson:"timeout" yaml:"timeout" json:"timeout"`
ApproveUsers []*User `bson:"approve_users" yaml:"approve_users" json:"approve_users"`
NeededApprovers int `bson:"needed_approvers" yaml:"needed_approvers" json:"needed_approvers"`
RejectOrApprove config.ApproveOrReject `bson:"reject_or_approve" yaml:"-" json:"reject_or_approve"`
Timeout int `bson:"timeout" yaml:"timeout" json:"timeout"`
ApproveUsers []*User `bson:"approve_users" yaml:"approve_users" json:"approve_users"`
FloatApproveUsers []*User `bson:"-" yaml:"flat_approve_users" json:"flat_approve_users"`
NeededApprovers int `bson:"needed_approvers" yaml:"needed_approvers" json:"needed_approvers"`
RejectOrApprove config.ApproveOrReject `bson:"reject_or_approve" yaml:"-" json:"reject_or_approve"`
// InstanceCode: native approval instance code, save for working after restart aslan
InstanceCode string `bson:"instance_code" yaml:"instance_code" json:"instance_code"`
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,16 +154,23 @@ func setCollaborationModesWorkflowDisplayName(mode *models.CollaborationMode) {
}

func setMemberInfo(mode *models.CollaborationMode) {
if mode.MemberInfo != nil && len(mode.MemberInfo) > 0 {
if mode.MemberInfo != nil && len(mode.MemberInfo) == len(mode.Members) {
return
}

memberList := make([]*types.Identity, 0)
memberInfoMap := make(map[string]*types.Identity)
for _, member := range mode.MemberInfo {
memberInfoMap[member.UID] = member
}

for _, uid := range mode.Members {
memberList = append(memberList, &types.Identity{
if _, ok := memberInfoMap[uid]; ok {
continue
}
mode.MemberInfo = append(mode.MemberInfo, &types.Identity{
IdentityType: "user",
UID: uid,
})
}
mode.MemberInfo = memberList

}
144 changes: 108 additions & 36 deletions pkg/microservice/aslan/core/release_plan/service/approval.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ import (
"time"

"github.com/google/uuid"
"github.com/koderover/zadig/v2/pkg/shared/client/systemconfig"
"github.com/koderover/zadig/v2/pkg/shared/client/user"
"github.com/koderover/zadig/v2/pkg/tool/mail"
"github.com/koderover/zadig/v2/pkg/types"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/util/sets"

configbase "github.com/koderover/zadig/v2/pkg/config"
"github.com/koderover/zadig/v2/pkg/microservice/aslan/config"
Expand All @@ -35,12 +40,9 @@ import (
approvalservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/approval"
dingservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/dingtalk"
larkservice "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/lark"
"github.com/koderover/zadig/v2/pkg/shared/client/systemconfig"
"github.com/koderover/zadig/v2/pkg/shared/client/user"
"github.com/koderover/zadig/v2/pkg/tool/dingtalk"
"github.com/koderover/zadig/v2/pkg/tool/lark"
"github.com/koderover/zadig/v2/pkg/tool/log"
"github.com/koderover/zadig/v2/pkg/tool/mail"
)

//go:embed approval.html
Expand Down Expand Up @@ -210,48 +212,114 @@ func updateDingTalkApproval(ctx context.Context, approvalInfo *models.Approval)
return nil
}

func geneFlatNativeApprovalUsers(approval *models.NativeApproval) ([]*models.User, map[string]*types.UserInfo) {
// change [group + user] approvals to user approvals
approvalUsers := make([]*models.User, 0)
userSet := sets.NewString()
userMap := make(map[string]*types.UserInfo)

if approval == nil {
return approvalUsers, userMap
}

for _, u := range approval.ApproveUsers {
if u.Type == "user" || u.Type == "" {
userSet.Insert(u.UserID)
approvalUsers = append(approvalUsers, u)
}
}
for _, u := range approval.ApproveUsers {
if u.Type == "group" {
groupInfo, err := user.New().GetGroupDetailedInfo(u.GroupID)
if err != nil {
log.Warnf("CreateNativeApproval GetGroupDetailedInfo error, error msg:%s", err)
continue
}
for _, uid := range groupInfo.UIDs {
if userSet.Has(uid) {
continue
}
userSet.Insert(uid)
userDetailedInfo, err := user.New().GetUserByID(uid)
if err != nil {
log.Errorf("failed to find user %s, error: %s", uid, err)
continue
}
userMap[uid] = userDetailedInfo
approvalUsers = append(approvalUsers, &models.User{
Type: "user",
UserID: uid,
UserName: userDetailedInfo.Name,
})
}
}
}

return approvalUsers, userMap
}

func createNativeApproval(plan *models.ReleasePlan, url string) error {
if plan == nil || plan.Approval == nil || plan.Approval.NativeApproval == nil {
return errors.New("createNativeApproval: native approval data not found")
}
approval := plan.Approval.NativeApproval

approvalUsers, userMap := geneFlatNativeApprovalUsers(approval)

// send email to all approval users if necessary
go func() {
email, err := systemconfig.New().GetEmailHost()
if err != nil {
log.Errorf("CreateNativeApproval GetEmailHost error, error msg:%s", err)
return
}
var err error
mailNotifyInfo := ""
var email *systemconfig.Email

t, err := template.New("approval").Parse(string(approvalHTML))
if err != nil {
log.Errorf("CreateNativeApproval template parse error, error msg:%s", err)
return
for {
email, err = systemconfig.New().GetEmailHost()
if err != nil {
log.Errorf("CreateNativeApproval GetEmailHost error, error msg:%s", err)
break
}

t, err := template.New("approval").Parse(string(approvalHTML))
if err != nil {
log.Errorf("CreateNativeApproval template parse error, error msg:%s", err)
break
}
var buf bytes.Buffer
err = t.Execute(&buf, struct {
PlanName string
Manager string
Description string
TimeRange string
Url string
}{
PlanName: plan.Name,
Manager: plan.Manager,
Description: plan.Description,
TimeRange: time.Unix(plan.StartTime, 0).Format("2006-01-02 15:04:05") + "-" + time.Unix(plan.EndTime, 0).Format("2006-01-02 15:04:05"),
Url: url,
})
if err != nil {
log.Errorf("CreateNativeApproval template execute error, error msg:%s", err)
break
}
mailNotifyInfo = buf.String()
break
}
var buf bytes.Buffer
err = t.Execute(&buf, struct {
PlanName string
Manager string
Description string
TimeRange string
Url string
}{
PlanName: plan.Name,
Manager: plan.Manager,
Description: plan.Description,
TimeRange: time.Unix(plan.StartTime, 0).Format("2006-01-02 15:04:05") + "-" + time.Unix(plan.EndTime, 0).Format("2006-01-02 15:04:05"),
Url: url,
})
if err != nil {
log.Errorf("CreateNativeApproval template execute error, error msg:%s", err)

if email == nil {
return
}
for _, u := range approval.ApproveUsers {
info, err := user.New().GetUserByID(u.UserID)
if err != nil {
log.Warnf("CreateNativeApproval GetUserByUid error, error msg:%s", err)
continue

for _, u := range approvalUsers {
info, ok := userMap[u.UserID]
if !ok {
info, err = user.New().GetUserByID(u.UserID)
if err != nil {
log.Warnf("CreateNativeApproval GetUserByUid error, error msg:%s", err)
continue
}
}

if info.Email == "" {
log.Warnf("CreateNativeApproval user %s email is empty", info.Name)
continue
Expand All @@ -264,7 +332,7 @@ func createNativeApproval(plan *models.ReleasePlan, url string) error {
UserName: email.UserName,
Password: email.Password,
Port: email.Port,
Body: buf.String(),
Body: mailNotifyInfo,
})
if err != nil {
log.Errorf("CreateNativeApproval SendEmail error, error msg:%s", err)
Expand All @@ -273,10 +341,14 @@ func createNativeApproval(plan *models.ReleasePlan, url string) error {
}
}()

originApprovalUser := approval.ApproveUsers
approval.ApproveUsers = approvalUsers

approveKey := uuid.New().String()
approval.InstanceCode = approveKey
approveWithL := approval
approvalservice.GlobalApproveMap.SetApproval(approveKey, approveWithL)

approvalservice.GlobalApproveMap.SetApproval(approveKey, approval)
approval.ApproveUsers = originApprovalUser
return nil
}

Expand Down
13 changes: 12 additions & 1 deletion pkg/microservice/aslan/core/release_plan/service/release_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,14 @@ func ListReleasePlans(pageNum, pageSize int64) (*ListReleasePlanResp, error) {
func GetReleasePlan(id string) (*models.ReleasePlan, error) {
releasePlan, err := mongodb.NewReleasePlanColl().GetByID(context.Background(), id)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "GetReleasePlan")
}

// native approval users may be user or user groups
// convert to flat user when needed, this data is generated dynamically because group binding may be changed
if releasePlan.Approval != nil && releasePlan.Approval.NativeApproval != nil {
flatNativeApprovalUsers, _ := geneFlatNativeApprovalUsers(releasePlan.Approval.NativeApproval)
releasePlan.Approval.NativeApproval.FloatApproveUsers = flatNativeApprovalUsers
}

for _, releasePlanJob := range releasePlan.Jobs {
Expand Down Expand Up @@ -533,7 +540,11 @@ func ApproveReleasePlan(c *handler.Context, planID string, req *ApproveRequest)
if !ok {
// restore data after restart aslan
log.Infof("updateNativeApproval: approval instance code %s not found, set it", plan.Approval.NativeApproval.InstanceCode)
approvalUsers, _ := geneFlatNativeApprovalUsers(plan.Approval.NativeApproval)
originApprovalUsers := plan.Approval.NativeApproval.ApproveUsers
plan.Approval.NativeApproval.ApproveUsers = approvalUsers
approvalservice.GlobalApproveMap.SetApproval(plan.Approval.NativeApproval.InstanceCode, plan.Approval.NativeApproval)
plan.Approval.NativeApproval.ApproveUsers = originApprovalUsers
}

approval, err = approvalservice.GlobalApproveMap.DoApproval(approvalKey, c.UserName, c.UserID, req.Comment, req.Approve)
Expand Down
Loading

0 comments on commit ba7c492

Please sign in to comment.