Changeset 0.27.3 (#170)
This commit is contained in:
@@ -7,8 +7,9 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.gobha.me/xcaliber/chat-switchboard/models"
|
||||
"git.gobha.me/xcaliber/chat-switchboard/taskutil"
|
||||
"git.gobha.me/xcaliber/chat-switchboard/store"
|
||||
"git.gobha.me/xcaliber/chat-switchboard/taskutil"
|
||||
"git.gobha.me/xcaliber/chat-switchboard/webhook"
|
||||
)
|
||||
|
||||
// TaskHandler manages task CRUD and manual execution.
|
||||
@@ -129,6 +130,11 @@ func (h *TaskHandler) Create(c *gin.Context) {
|
||||
|
||||
t.IsActive = true
|
||||
|
||||
// v0.27.3: Generate webhook secret if webhook URL is provided
|
||||
if t.WebhookURL != "" && t.WebhookSecret == "" {
|
||||
t.WebhookSecret = webhook.GenerateSecret()
|
||||
}
|
||||
|
||||
if err := h.stores.Tasks.Create(ctx, &t); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create task: " + err.Error()})
|
||||
return
|
||||
|
||||
@@ -355,103 +355,8 @@ func (h *WorkflowInstanceHandler) emitWorkflowEvent(label, channelID string, dat
|
||||
// triggerOnComplete checks if the workflow has an on_complete chain config
|
||||
// and starts the target workflow if so.
|
||||
func (h *WorkflowInstanceHandler) triggerOnComplete(ctx context.Context, workflowID, channelID, mergedData string) {
|
||||
if h.stores.Workflows == nil {
|
||||
return
|
||||
}
|
||||
wf, err := h.stores.Workflows.GetByID(ctx, workflowID)
|
||||
if err != nil || wf == nil || len(wf.OnComplete) == 0 || string(wf.OnComplete) == "null" {
|
||||
return
|
||||
}
|
||||
|
||||
var chain struct {
|
||||
Action string `json:"action"`
|
||||
TargetSlug string `json:"target_slug"`
|
||||
DataMap map[string]string `json:"data_map"` // source_key → target_key
|
||||
}
|
||||
if err := json.Unmarshal(wf.OnComplete, &chain); err != nil || chain.Action != "start_workflow" || chain.TargetSlug == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// Look up target workflow
|
||||
target, err := h.stores.Workflows.GetBySlug(ctx, wf.TeamID, chain.TargetSlug)
|
||||
if err != nil || target == nil || !target.IsActive {
|
||||
log.Printf("[workflow] on_complete: target workflow '%s' not found or inactive", chain.TargetSlug)
|
||||
return
|
||||
}
|
||||
|
||||
// Map stage_data from completed workflow to initial stage_data for target
|
||||
var sourceData map[string]any
|
||||
if err := json.Unmarshal([]byte(mergedData), &sourceData); err != nil {
|
||||
sourceData = map[string]any{}
|
||||
}
|
||||
targetData := map[string]any{}
|
||||
if len(chain.DataMap) > 0 {
|
||||
for srcKey, tgtKey := range chain.DataMap {
|
||||
if v, ok := sourceData[srcKey]; ok {
|
||||
targetData[tgtKey] = v
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No mapping = pass all data through
|
||||
targetData = sourceData
|
||||
}
|
||||
|
||||
// Get the channel owner to start the chained workflow as
|
||||
var ownerID string
|
||||
_ = database.DB.QueryRowContext(ctx, database.Q(`
|
||||
SELECT user_id FROM channels WHERE id = $1
|
||||
`), channelID).Scan(&ownerID)
|
||||
if ownerID == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// Create a new workflow channel for the target
|
||||
ver, err := h.stores.Workflows.GetLatestVersion(ctx, target.ID)
|
||||
if err != nil {
|
||||
log.Printf("[workflow] on_complete: target '%s' has no published version", chain.TargetSlug)
|
||||
return
|
||||
}
|
||||
stages, err := h.stores.Workflows.ListStages(ctx, target.ID)
|
||||
if err != nil || len(stages) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
ch := &models.Channel{
|
||||
UserID: ownerID,
|
||||
Title: target.Name + " (chained)",
|
||||
Description: target.Description,
|
||||
Type: "workflow",
|
||||
TeamID: target.TeamID,
|
||||
}
|
||||
if err := h.stores.Channels.Create(ctx, ch); err != nil {
|
||||
log.Printf("[workflow] on_complete: failed to create chained channel: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
initialData, _ := json.Marshal(targetData)
|
||||
_, err = database.DB.ExecContext(ctx, database.Q(`
|
||||
UPDATE channels
|
||||
SET workflow_id = $1, workflow_version = $2, current_stage = 0,
|
||||
stage_data = $3, workflow_status = 'active',
|
||||
last_activity_at = $4, ai_mode = 'auto'
|
||||
WHERE id = $5
|
||||
`), target.ID, ver.VersionNumber, string(initialData), time.Now().UTC(), ch.ID)
|
||||
if err != nil {
|
||||
log.Printf("[workflow] on_complete: failed to set workflow columns: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
_ = h.stores.Channels.AddParticipant(ctx, &models.ChannelParticipant{
|
||||
ChannelID: ch.ID, ParticipantType: "user", ParticipantID: ownerID, Role: "owner",
|
||||
})
|
||||
if stages[0].PersonaID != nil {
|
||||
_ = h.stores.Channels.AddParticipant(ctx, &models.ChannelParticipant{
|
||||
ChannelID: ch.ID, ParticipantType: "persona", ParticipantID: *stages[0].PersonaID, Role: "member",
|
||||
})
|
||||
}
|
||||
|
||||
log.Printf("[workflow] on_complete: chained '%s' → '%s' (channel %s → %s)",
|
||||
wf.Slug, target.Slug, channelID, ch.ID)
|
||||
// v0.27.3: Delegate to shared implementation (also handles webhooks)
|
||||
go tools.TriggerWorkflowOnComplete(ctx, h.stores, workflowID, channelID, mergedData)
|
||||
}
|
||||
|
||||
// tryRoundRobin checks if the stage has auto_assign:"round_robin" in
|
||||
|
||||
Reference in New Issue
Block a user