init push
This commit is contained in:
141
internal/service/mock_email_sender.go
Normal file
141
internal/service/mock_email_sender.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"wucher/internal/shared/pkg/uuidv7"
|
||||
"wucher/internal/queue"
|
||||
)
|
||||
|
||||
type MockEmailSender struct {
|
||||
dir string
|
||||
}
|
||||
|
||||
type MockEmailRecord struct {
|
||||
StoredAt time.Time `json:"stored_at"`
|
||||
Provider string `json:"provider"`
|
||||
JobID string `json:"job_id"`
|
||||
MessageType string `json:"message_type"`
|
||||
To string `json:"to"`
|
||||
CC []string `json:"cc,omitempty"`
|
||||
BCC []string `json:"bcc,omitempty"`
|
||||
ReplyTo []string `json:"reply_to,omitempty"`
|
||||
Subject string `json:"subject"`
|
||||
TextBody string `json:"text_body,omitempty"`
|
||||
HTMLBody string `json:"html_body,omitempty"`
|
||||
Tags map[string]string `json:"tags,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
TraceID string `json:"trace_id,omitempty"`
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
}
|
||||
|
||||
func NewMockEmailSender(dir string) *MockEmailSender {
|
||||
return &MockEmailSender{dir: strings.TrimSpace(dir)}
|
||||
}
|
||||
|
||||
func (s *MockEmailSender) Configured() bool {
|
||||
return s != nil && strings.TrimSpace(s.dir) != ""
|
||||
}
|
||||
|
||||
func (s *MockEmailSender) Send(ctx context.Context, job queue.EmailJob) (EmailDeliveryResult, error) {
|
||||
if !s.Configured() {
|
||||
return EmailDeliveryResult{}, errors.New("mock email sender is not configured")
|
||||
}
|
||||
job = job.Canonical()
|
||||
if err := job.Validate(); err != nil {
|
||||
return EmailDeliveryResult{}, err
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return EmailDeliveryResult{}, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(s.dir, 0o755); err != nil {
|
||||
return EmailDeliveryResult{}, err
|
||||
}
|
||||
|
||||
jobID := strings.TrimSpace(job.JobID)
|
||||
if jobID == "" {
|
||||
var err error
|
||||
jobID, err = uuidv7.String(uuidv7.MustNew())
|
||||
if err != nil {
|
||||
return EmailDeliveryResult{}, err
|
||||
}
|
||||
}
|
||||
filename := time.Now().UTC().Format("20060102T150405.000000000Z") + "_" + sanitizeFileName(job.NormalizedType()) + "_" + sanitizeFileName(jobID) + ".json"
|
||||
path := filepath.Join(s.dir, filename)
|
||||
|
||||
record := MockEmailRecord{
|
||||
StoredAt: time.Now().UTC(),
|
||||
Provider: "mock",
|
||||
JobID: jobID,
|
||||
MessageType: job.NormalizedType(),
|
||||
To: job.To,
|
||||
CC: append([]string(nil), job.CC...),
|
||||
BCC: append([]string(nil), job.BCC...),
|
||||
ReplyTo: append([]string(nil), job.ReplyTo...),
|
||||
Subject: job.Subject,
|
||||
TextBody: job.TextBodyValue(),
|
||||
HTMLBody: job.HTMLBody,
|
||||
Tags: cloneStringMap(job.Tags),
|
||||
Metadata: cloneStringMap(job.Metadata),
|
||||
TraceID: job.TraceID,
|
||||
RequestID: job.RequestID,
|
||||
}
|
||||
payload, err := json.MarshalIndent(record, "", " ")
|
||||
if err != nil {
|
||||
return EmailDeliveryResult{}, err
|
||||
}
|
||||
payload = append(payload, '\n')
|
||||
|
||||
if err := os.WriteFile(path, payload, 0o600); err != nil {
|
||||
return EmailDeliveryResult{}, err
|
||||
}
|
||||
return EmailDeliveryResult{
|
||||
Provider: "mock",
|
||||
ProviderMessageID: filename,
|
||||
RequestID: path,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func sanitizeFileName(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return "email"
|
||||
}
|
||||
var b strings.Builder
|
||||
b.Grow(len(value))
|
||||
for _, r := range value {
|
||||
switch {
|
||||
case r >= 'a' && r <= 'z':
|
||||
b.WriteRune(r)
|
||||
case r >= 'A' && r <= 'Z':
|
||||
b.WriteRune(r)
|
||||
case r >= '0' && r <= '9':
|
||||
b.WriteRune(r)
|
||||
case r == '-', r == '_':
|
||||
b.WriteRune(r)
|
||||
default:
|
||||
b.WriteRune('_')
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func cloneStringMap(values map[string]string) map[string]string {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]string, len(values))
|
||||
for key, value := range values {
|
||||
out[key] = value
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user