Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions pkg/microservice/aslan/core/common/repository/models/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type SystemSetting struct {
Privacy *PrivacySettings `bson:"privacy" json:"privacy"`
Language string `bson:"language" json:"language"`
ServerURL string `bson:"server_url" json:"server_url"`
WorkflowHook *WorkflowHookSettings `bson:"workflow_hook" json:"workflow_hook"`
ReleasePlanHook *ReleasePlanHookSettings `bson:"release_plan_hook" json:"release_plan_hook"`
UpdateTime int64 `bson:"update_time" json:"update_time"`
}
Expand Down Expand Up @@ -81,6 +82,13 @@ type ReleasePlanHookSettings struct {
HookEvents []ReleasePlanHookEvent `json:"hook_events" bson:"hook_events"`
}

type WorkflowHookSettings struct {
Enable bool `json:"enable" bson:"enable"`
HookAddress string `json:"hook_address" bson:"hook_address"`
HookSecret string `json:"hook_secret" bson:"hook_secret"`
HookEvents []WorkflowHookEvent `json:"hook_events" bson:"hook_events"`
}

func (r *ReleasePlanHookSettings) ToHookSettings() *HookSettings {
return &HookSettings{
Enable: r.Enable,
Expand All @@ -98,6 +106,13 @@ const (
ReleasePlanHookEventAllJobDone ReleasePlanHookEvent = "all_job_done"
)

type WorkflowHookEvent string

const (
WorkflowHookEventStartExecute WorkflowHookEvent = "start_execute"
WorkflowHookEventCompleteExecute WorkflowHookEvent = "complete_execute"
)

func (SystemSetting) TableName() string {
return "system_setting"
}
33 changes: 33 additions & 0 deletions pkg/microservice/aslan/core/common/repository/mongodb/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,36 @@ func (c *SystemSettingColl) GetReleasePlanHookSetting() (*models.ReleasePlanHook

return resp.ReleasePlanHook, nil
}

func (c *SystemSettingColl) UpdateWorkflowHookSetting(hookSetting *models.WorkflowHookSettings) error {
id, _ := primitive.ObjectIDFromHex(setting.LocalClusterID)
query := bson.M{"_id": id}

change := bson.M{"$set": bson.M{"workflow_hook": hookSetting}}

_, err := c.UpdateOne(context.TODO(), query, change)
return err
}

func (c *SystemSettingColl) GetWorkflowHookSetting() (*models.WorkflowHookSettings, error) {
query := bson.M{}
resp := &models.SystemSetting{}

err := c.FindOne(context.TODO(), query).Decode(resp)
Comment thread
huanghongbo-hhb marked this conversation as resolved.
if err != nil {
if err == mongo.ErrNoDocuments {
return &models.WorkflowHookSettings{
Enable: false,
}, nil
}
return nil, err
}

if resp.WorkflowHook == nil {
return &models.WorkflowHookSettings{
Enable: false,
}, nil
}

return resp.WorkflowHook, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,18 @@ func getStageTaskByName(stages []*models.StageTask, stageName string) *models.St
return nil
}

func buildWorkflowNotifyReleasePlan(releasePlan *models.ReleasePlanRef) *webhooknotify.WorkflowNotifyReleasePlan {
if releasePlan == nil {
return nil
}

return &webhooknotify.WorkflowNotifyReleasePlan{
ID: releasePlan.ID,
Name: releasePlan.Name,
Index: releasePlan.Index,
}
}

func (w *Service) getApproveNotificationContent(notify *models.NotifyCtl, task *models.WorkflowTask) (string, string, *LarkCard, *webhooknotify.WorkflowNotify, error) {
project, err := templaterepo.NewProductColl().Find(task.ProjectName)
if err != nil {
Expand Down Expand Up @@ -774,6 +786,7 @@ func (w *Service) getApproveNotificationContent(notify *models.NotifyCtl, task *
WorkflowDisplayName: task.WorkflowDisplayName,
ProjectName: task.ProjectName,
ProjectDisplayName: project.ProjectName,
ReleasePlan: buildWorkflowNotifyReleasePlan(task.ReleasePlan),
Status: task.Status,
Remark: task.Remark,
Error: task.Error,
Expand All @@ -782,7 +795,6 @@ func (w *Service) getApproveNotificationContent(notify *models.NotifyCtl, task *
EndTime: task.EndTime,
TaskCreator: task.TaskCreator,
TaskCreatorID: task.TaskCreatorID,
TaskCreatorPhone: task.TaskCreatorPhone,
TaskCreatorEmail: task.TaskCreatorEmail,
}

Expand Down Expand Up @@ -931,6 +943,56 @@ func (w *Service) getNotificationContent(notify *models.NotifyCtl, task *models.
return w.getNotificationContentWithOptions(notify, task, nil)
}

func (w *Service) BuildWorkflowWebhookNotify(task *models.WorkflowTask) (*webhooknotify.WorkflowNotify, error) {
_, _, _, webhookNotify, err := w.getNotificationContentWithOptions(&models.NotifyCtl{WebHookType: setting.NotifyWebHookTypeWebook}, task, nil)
if err != nil {
return nil, err
}
if webhookNotify == nil {
return nil, fmt.Errorf("failed to build workflow webhook payload for workflow %s, taskID: %d", task.WorkflowName, task.TaskID)
}

return webhookNotify, nil
}

func (w *Service) SendSystemWorkflowHook(task *models.WorkflowTask, hookSetting *models.WorkflowHookSettings, hookEvent models.WorkflowHookEvent) error {
if task == nil || !isWorkflowHookEventEnabled(hookSetting, hookEvent) {
return nil
}

webhookNotify, err := w.BuildWorkflowWebhookNotify(task)
if err != nil {
return err
}

return webhooknotify.NewClient(hookSetting.HookAddress, hookSetting.HookSecret).SendWorkflowWebhook(webhookNotify, workflowHookEventToWebhookEvent(hookEvent))
}

func isWorkflowHookEventEnabled(hookSetting *models.WorkflowHookSettings, hookEvent models.WorkflowHookEvent) bool {
if hookSetting == nil || !hookSetting.Enable {
return false
}

for _, configuredEvent := range hookSetting.HookEvents {
if configuredEvent == hookEvent {
return true
}
}

return false
}

func workflowHookEventToWebhookEvent(hookEvent models.WorkflowHookEvent) webhooknotify.WebHookNotifyEvent {
switch hookEvent {
case models.WorkflowHookEventStartExecute:
return webhooknotify.WebHookNotifyEventWorkflowStartExecute
case models.WorkflowHookEventCompleteExecute:
return webhooknotify.WebHookNotifyEventWorkflowCompleteExecute
default:
return webhooknotify.WebHookNotifyEventWorkflow
}
}

type workflowNotificationOptions struct {
StatusTextKeyOverride string
PendingStageName string
Expand Down Expand Up @@ -973,6 +1035,7 @@ func (w *Service) getNotificationContentWithOptions(notify *models.NotifyCtl, ta
WorkflowDisplayName: task.WorkflowDisplayName,
ProjectName: task.ProjectName,
ProjectDisplayName: project.ProjectName,
ReleasePlan: buildWorkflowNotifyReleasePlan(task.ReleasePlan),
Status: task.Status,
Remark: task.Remark,
Error: task.Error,
Expand All @@ -981,7 +1044,6 @@ func (w *Service) getNotificationContentWithOptions(notify *models.NotifyCtl, ta
EndTime: task.EndTime,
TaskCreator: task.TaskCreator,
TaskCreatorID: task.TaskCreatorID,
TaskCreatorPhone: task.TaskCreatorPhone,
TaskCreatorEmail: task.TaskCreatorEmail,
TaskType: task.Type,
}
Expand Down Expand Up @@ -1465,7 +1527,7 @@ func (w *Service) sendNotification(title, content string, notify *models.NotifyC
}
case setting.NotifyWebHookTypeWebook:
webhookclient := webhooknotify.NewClient(notify.WebhookNotificationConfig.Address, notify.WebhookNotificationConfig.Token)
err := webhookclient.SendWorkflowWebhook(webhookNotify)
err := webhookclient.SendWorkflowWebhook(webhookNotify, webhooknotify.WebHookNotifyEventWorkflow)
if err != nil {
return fmt.Errorf("failed to send notification to webhook, address %s, token: %s, error: %v", notify.WebhookNotificationConfig.Address, notify.WebhookNotificationConfig.Token, err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ func NewClient(address, token string) *webhookNotifyclient {
}
}

func (c *webhookNotifyclient) SendWorkflowWebhook(webhookNotify *WorkflowNotify) error {
func (c *webhookNotifyclient) SendWorkflowWebhook(webhookNotify *WorkflowNotify, event WebHookNotifyEvent) error {
notify := &WebHookNotify{
ObjectKind: WebHookNotifyObjectKindWorkflow,
Event: WebHookNotifyEventWorkflow,
Event: event,
Workflow: webhookNotify,
}
return c.sendWebhook(notify)
}

func (c *webhookNotifyclient) SendReleasePlanWebhook(webhookNotify *ReleasePlanHookBody) error {
func (c *webhookNotifyclient) SendReleasePlanWebhook(webhookNotify *ReleasePlanHookBody, event WebHookNotifyEvent) error {
notify := &WebHookNotify{
ObjectKind: WebHookNotifyObjectKindReleasePlan,
Event: WebHookNotifyEventReleasePlan,
Event: event,
ReleasePlan: webhookNotify,
}
return c.sendWebhook(notify)
Expand Down
17 changes: 14 additions & 3 deletions pkg/microservice/aslan/core/common/service/webhooknotify/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,13 @@ const (
type WebHookNotifyEvent string

const (
WebHookNotifyEventWorkflow WebHookNotifyEvent = "workflow"
WebHookNotifyEventReleasePlan WebHookNotifyEvent = "release_plan"
WebHookNotifyEventWorkflow WebHookNotifyEvent = "workflow"
WebHookNotifyEventWorkflowStartExecute WebHookNotifyEvent = "start_execute"
WebHookNotifyEventWorkflowCompleteExecute WebHookNotifyEvent = "complete_execute"
WebHookNotifyEventReleasePlan WebHookNotifyEvent = "release_plan"
WebHookNotifyEventFinishPlanning WebHookNotifyEvent = "finish_planning"
WebHookNotifyEventReleasePlanStartExecute WebHookNotifyEvent = "start_execute"
WebHookNotifyEventAllJobDone WebHookNotifyEvent = "all_job_done"
)

type WebHookNotifyObjectKind string
Expand All @@ -62,6 +67,7 @@ type WorkflowNotify struct {
ProjectDisplayName string `json:"project_display_name"`
WorkflowName string `json:"workflow_name"`
WorkflowDisplayName string `json:"workflow_display_name"`
ReleasePlan *WorkflowNotifyReleasePlan `json:"release_plan,omitempty"`
Status config.Status `json:"status"`
Remark string `json:"remark"`
DetailURL string `json:"detail_url"`
Expand All @@ -72,11 +78,16 @@ type WorkflowNotify struct {
Stages []*WorkflowNotifyStage `json:"stages"`
TaskCreator string `json:"task_creator"`
TaskCreatorID string `json:"task_creator_id"`
TaskCreatorPhone string `json:"task_creator_phone"`
TaskCreatorEmail string `json:"task_creator_email"`
TaskType config.CustomWorkflowTaskType `json:"task_type"`
}

type WorkflowNotifyReleasePlan struct {
ID string `json:"id"`
Name string `json:"name"`
Index int64 `json:"index"`
}

type WorkflowNotifyStage struct {
Name string `json:"name"`
Status config.Status `json:"status"`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright 2025 The KodeRover Authors.

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 workflowcontroller

import (
commonmodels "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/models"
commonrepo "github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/repository/mongodb"
"github.com/koderover/zadig/v2/pkg/microservice/aslan/core/common/service/instantmessage"
)

func SendSystemWorkflowHook(task *commonmodels.WorkflowTask, hookEvent commonmodels.WorkflowHookEvent) error {
hookSetting, err := commonrepo.NewSystemSettingColl().GetWorkflowHookSetting()
if err != nil {
return err
}

if err := instantmessage.NewWeChatClient().SendSystemWorkflowHook(task, hookSetting, hookEvent); err != nil {
return err
}

return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@ func (c *workflowCtl) updateWorkflowTask() {
}

c.workflowTask.Remark = ""
shouldSendCompleteHook := c.workflowTask.Finished() && taskInColl.EndTime == 0 && c.workflowTask.EndTime > 0

c.workflowTaskMutex.Lock()
if err := commonrepo.NewworkflowTaskv4Coll().Update(c.workflowTask.ID.Hex(), c.workflowTask); err != nil {
Expand All @@ -570,6 +571,11 @@ func (c *workflowCtl) updateWorkflowTask() {
if err := instantmessage.NewWeChatClient().SendWorkflowTaskNotifications(c.workflowTask); err != nil {
c.logger.Errorf("send workflow task notification failed, error: %v", err)
}
if shouldSendCompleteHook {
if err := SendSystemWorkflowHook(c.workflowTask, commonmodels.WorkflowHookEventCompleteExecute); err != nil {
c.logger.Errorf("send system workflow complete hook failed, workflow: %s, taskID: %d, error: %v", c.workflowTask.WorkflowName, c.workflowTask.TaskID, err)
}
}
q := ConvertTaskToQueue(c.workflowTask)
if err := Remove(q); err != nil {
c.logger.Errorf("remove queue task: %s:%d error: %v", c.workflowTask.WorkflowName, c.workflowTask.TaskID, err)
Expand Down
15 changes: 14 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 @@ -1734,7 +1734,7 @@ func sendReleasePlanHook(plan *models.ReleasePlan, systemHookSetting *commonmode
return err
}

err = webhooknotify.NewClient(systemHookSetting.HookAddress, systemHookSetting.HookSecret).SendReleasePlanWebhook(hookBody)
err = webhooknotify.NewClient(systemHookSetting.HookAddress, systemHookSetting.HookSecret).SendReleasePlanWebhook(hookBody, releasePlanHookEventToWebhookEvent(hookBody.EventName))
if err != nil {
err = errors.Wrap(err, "send release plan hook")
log.Error(err)
Expand All @@ -1745,6 +1745,19 @@ func sendReleasePlanHook(plan *models.ReleasePlan, systemHookSetting *commonmode
return nil
}

func releasePlanHookEventToWebhookEvent(hookEvent commonmodels.ReleasePlanHookEvent) webhooknotify.WebHookNotifyEvent {
switch hookEvent {
case commonmodels.ReleasePlanHookEventFinishPlanning:
return webhooknotify.WebHookNotifyEventFinishPlanning
case commonmodels.ReleasePlanHookEventStartExecute:
return webhooknotify.WebHookNotifyEventReleasePlanStartExecute
case commonmodels.ReleasePlanHookEventAllJobDone:
return webhooknotify.WebHookNotifyEventAllJobDone
default:
return webhooknotify.WebHookNotifyEventReleasePlan
}
}

func convertReleasePlanToHookBody(plan *models.ReleasePlan, hookEvent commonmodels.ReleasePlanHookEvent) (*webhooknotify.ReleasePlanHookBody, error) {
hookBody := &webhooknotify.ReleasePlanHookBody{
ID: plan.ID,
Expand Down
6 changes: 6 additions & 0 deletions pkg/microservice/aslan/core/system/handler/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,12 @@ func (*Router) Inject(router *gin.RouterGroup) {
server.PUT("/url", SetSystemServerURL)
}

workflowHook := router.Group("workflowHook")
{
workflowHook.GET("", GetWorkflowHookSetting)
workflowHook.PUT("", UpdateWorkflowHookSetting)
}

// ---------------------------------------------------------------------------------------
// external system API
// ---------------------------------------------------------------------------------------
Expand Down
Loading