feat(automation): align attachment uploads
This commit is contained in:
@@ -2,9 +2,12 @@ package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
"github.com/gochat/gochat/internal/worker"
|
||||
applogger "github.com/gochat/gochat/pkg/logger"
|
||||
"gorm.io/gorm"
|
||||
@@ -44,6 +47,7 @@ func (s *AutomationRuleService) GetByID(ctx context.Context, id uint) (*Automati
|
||||
if err := s.db.DB().WithContext(ctx).First(&rule, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = s.hydrateAutomationRuleFiles(ctx, &rule)
|
||||
return &rule, nil
|
||||
}
|
||||
|
||||
@@ -55,6 +59,7 @@ func (s *AutomationRuleService) GetByIDForAccount(ctx context.Context, accountID
|
||||
First(&rule, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = s.hydrateAutomationRuleFiles(ctx, &rule)
|
||||
return &rule, nil
|
||||
}
|
||||
|
||||
@@ -67,6 +72,9 @@ func (s *AutomationRuleService) ListByAccount(ctx context.Context, accountID uin
|
||||
Find(&rules).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range rules {
|
||||
_ = s.hydrateAutomationRuleFiles(ctx, &rules[i])
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
@@ -91,16 +99,22 @@ func (s *AutomationRuleService) Create(ctx context.Context, rule *AutomationRule
|
||||
if err := ValidateConditions(rule.Conditions); err != nil {
|
||||
return fmt.Errorf("invalid conditions: %w", err)
|
||||
}
|
||||
if err := s.normalizeAutomationRuleAttachmentActions(ctx, rule.AccountID, &rule.Actions); err != nil {
|
||||
return err
|
||||
}
|
||||
// Validate actions before saving
|
||||
if err := ValidateActions(rule.Actions); err != nil {
|
||||
return fmt.Errorf("invalid actions: %w", err)
|
||||
}
|
||||
// Use Select to force all fields including zero-value bool Active=false
|
||||
// Without Select, GORM skips zero-value fields and uses column defaults.
|
||||
return s.db.DB().WithContext(ctx).Select(
|
||||
if err := s.db.DB().WithContext(ctx).Select(
|
||||
"AccountID", "EventName", "Name", "Description",
|
||||
"Conditions", "Actions", "Active",
|
||||
).Create(rule).Error
|
||||
).Create(rule).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return s.hydrateAutomationRuleFiles(ctx, rule)
|
||||
}
|
||||
|
||||
// Update updates an existing automation rule.
|
||||
@@ -110,6 +124,9 @@ func (s *AutomationRuleService) Update(ctx context.Context, rule *AutomationRule
|
||||
if err := ValidateConditions(rule.Conditions); err != nil {
|
||||
return fmt.Errorf("invalid conditions: %w", err)
|
||||
}
|
||||
if err := s.normalizeAutomationRuleAttachmentActions(ctx, rule.AccountID, &rule.Actions); err != nil {
|
||||
return err
|
||||
}
|
||||
// Validate actions before saving
|
||||
if err := ValidateActions(rule.Actions); err != nil {
|
||||
return fmt.Errorf("invalid actions: %w", err)
|
||||
@@ -130,12 +147,100 @@ func (s *AutomationRuleService) UpdateForAccount(ctx context.Context, accountID
|
||||
existing.Description = rule.Description
|
||||
existing.EventName = rule.EventName
|
||||
existing.Conditions = NormalizeConditions(rule.Conditions)
|
||||
existing.Actions = rule.Actions
|
||||
if rule.Actions != nil {
|
||||
if err := s.normalizeAutomationRuleAttachmentActions(ctx, accountID, &rule.Actions); err != nil {
|
||||
return err
|
||||
}
|
||||
existing.Actions = rule.Actions
|
||||
}
|
||||
existing.Active = rule.Active
|
||||
|
||||
return s.Update(ctx, &existing)
|
||||
}
|
||||
|
||||
func (s *AutomationRuleService) normalizeAutomationRuleAttachmentActions(ctx context.Context, accountID uint, actions *Actions) error {
|
||||
if actions == nil {
|
||||
return nil
|
||||
}
|
||||
for i := range *actions {
|
||||
action := &(*actions)[i]
|
||||
if action.ActionName != "send_attachment" {
|
||||
continue
|
||||
}
|
||||
if action.ActionParams == nil {
|
||||
return fmt.Errorf("invalid attachment")
|
||||
}
|
||||
upload, err := s.findAutomationRuleUpload(ctx, accountID, firstAutomationRuleBlobID(action.ActionParams))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid attachment")
|
||||
}
|
||||
action.ActionParams["blob_id"] = upload.ID
|
||||
delete(action.ActionParams, "attachment_url")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func firstAutomationRuleBlobID(params map[string]interface{}) interface{} {
|
||||
if value := params["blob_id"]; value != nil {
|
||||
return value
|
||||
}
|
||||
return params["attachment_url"]
|
||||
}
|
||||
|
||||
func (s *AutomationRuleService) findAutomationRuleUpload(ctx context.Context, accountID uint, value interface{}) (*model.DirectUpload, error) {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
if parsed, err := strconv.ParseUint(v, 10, 64); err == nil {
|
||||
return s.findAutomationRuleUploadByID(ctx, accountID, uint(parsed))
|
||||
}
|
||||
var upload model.DirectUpload
|
||||
if err := s.db.DB().WithContext(ctx).Where("account_id = ? AND upload_uuid = ?", accountID, v).First(&upload).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &upload, nil
|
||||
case float64:
|
||||
return s.findAutomationRuleUploadByID(ctx, accountID, uint(v))
|
||||
case int:
|
||||
return s.findAutomationRuleUploadByID(ctx, accountID, uint(v))
|
||||
case uint:
|
||||
return s.findAutomationRuleUploadByID(ctx, accountID, v)
|
||||
case json.Number:
|
||||
parsed, err := strconv.ParseUint(string(v), 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.findAutomationRuleUploadByID(ctx, accountID, uint(parsed))
|
||||
default:
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AutomationRuleService) findAutomationRuleUploadByID(ctx context.Context, accountID, id uint) (*model.DirectUpload, error) {
|
||||
var upload model.DirectUpload
|
||||
if err := s.db.DB().WithContext(ctx).Where("account_id = ? AND id = ?", accountID, id).First(&upload).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &upload, nil
|
||||
}
|
||||
|
||||
func (s *AutomationRuleService) hydrateAutomationRuleFiles(ctx context.Context, rule *AutomationRule) error {
|
||||
ids := macroAttachmentBlobIDs(rule.Actions)
|
||||
if len(ids) == 0 {
|
||||
rule.Files = nil
|
||||
return nil
|
||||
}
|
||||
var uploads []model.DirectUpload
|
||||
if err := s.db.DB().WithContext(ctx).Where("account_id = ? AND id IN ?", rule.AccountID, ids).Order("id ASC").Find(&uploads).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
files := make([]AutomationRuleFile, 0, len(uploads))
|
||||
for _, upload := range uploads {
|
||||
files = append(files, AutomationRuleFile{ID: upload.ID, AutomationRuleID: rule.ID, FileType: upload.MimeType, AccountID: upload.AccountID, FileURL: upload.FileURL, BlobID: upload.ID, Filename: upload.OriginalName})
|
||||
}
|
||||
rule.Files = files
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete soft-deletes an automation rule.
|
||||
func (s *AutomationRuleService) Delete(ctx context.Context, id uint) error {
|
||||
return s.db.DB().WithContext(ctx).Delete(&AutomationRule{}, id).Error
|
||||
|
||||
Reference in New Issue
Block a user