Files
warmbly/internal/app/campaign/service.go
T
Matthew Meszaros 03dc3bce95 feat: overhaul campaign creation with atomic transactional create + multi-step wizard
- fix 500 on POST /campaigns: insert now sets NOT NULL created_at/updated_at
- atomic create writes campaign + sequences + ab variants + tag/folder links + advanced overrides in one tx
- create accepts full optional config (schedule, sender pool, tracking, sequences, variants)
- handler forwards orgID so future start/stop and org-scoped routes resolve
- fix Get/GetByID joining campaign_email_tags/campaign_folders on wrong column
- publish CAMPAIGN_CREATED realtime event after successful create
- replace name+description modal with 4-step wizard (basics, schedule, sender pool, first email + follow-ups)
2026-05-24 16:13:22 +00:00

57 lines
2.0 KiB
Go

package campaign
import (
"context"
"github.com/google/uuid"
"github.com/warmbly/warmbly/internal/app/feature"
"github.com/warmbly/warmbly/internal/errx"
"github.com/warmbly/warmbly/internal/infrastructure/pubsub"
"github.com/warmbly/warmbly/internal/models"
"github.com/warmbly/warmbly/internal/repository"
)
const campaignCooldownSeconds = 60
type CampaignService interface {
Create(ctx context.Context, userID string, orgID *uuid.UUID, data *models.CreateCampaign) (*models.Campaign, *errx.Error)
Get(ctx context.Context, userID, id string) (*models.Campaign, *errx.Error)
Search(ctx context.Context, userID, query, cursor, folder, limit string) (*models.CampaignsResult, *errx.Error)
Update(ctx context.Context, userID, id string, data *models.UpdateCampaign) (*models.Campaign, *errx.Error)
Delete(ctx context.Context, userID, id string) *errx.Error
// Start/Stop
StartCampaign(ctx context.Context, orgID uuid.UUID, campaignID string) *errx.Error
StopCampaign(ctx context.Context, orgID uuid.UUID, campaignID string) *errx.Error
// Logs
GetLogs(ctx context.Context, userID, campaignID string, limit int, cursor *string) (*models.CampaignLogsResult, *errx.Error)
}
type campaignService struct {
campaignRepository repository.CampaignRepository
taskRepo repository.TaskRepository
emailRepo repository.EmailRepository
campaignLogRepo repository.CampaignLogRepository
featureGate feature.FeatureGateService
streamingPublisher *pubsub.StreamingPublisher
}
func NewService(
campaignRepository repository.CampaignRepository,
taskRepo repository.TaskRepository,
emailRepo repository.EmailRepository,
campaignLogRepo repository.CampaignLogRepository,
featureGate feature.FeatureGateService,
streamingPublisher *pubsub.StreamingPublisher,
) CampaignService {
return &campaignService{
campaignRepository: campaignRepository,
taskRepo: taskRepo,
emailRepo: emailRepo,
campaignLogRepo: campaignLogRepo,
featureGate: featureGate,
streamingPublisher: streamingPublisher,
}
}