Simplify sendEmail to rely on hooks for job creation

🔄 Cleaner Architecture:
- sendEmail now just creates the email and lets hooks handle job creation
- Hooks automatically create and populate job relationship
- For processImmediately, retrieves job from relationship and runs it
- Removes duplicate job creation logic from sendEmail

📈 Benefits:
- Single source of truth for job creation (hooks)
- Consistent behavior across all email creation methods
- Simpler, more maintainable code
- Better separation of concerns

🔍 Flow:
1. sendEmail creates email document
2. Hooks auto-create job and populate relationship
3. If processImmediately, fetch job from relationship and run it
4. Return email with complete job relationship
This commit is contained in:
2025-09-14 21:06:47 +02:00
parent 2d270ca527
commit 4e96fbcd20

View File

@@ -132,6 +132,7 @@ export const sendEmail = async <TEmail extends BaseEmailDocument = BaseEmailDocu
} }
// Create the email in the collection with proper typing // Create the email in the collection with proper typing
// The hooks will automatically create and populate the job relationship
const email = await payload.create({ const email = await payload.create({
collection: collectionSlug, collection: collectionSlug,
data: emailData data: emailData
@@ -142,54 +143,34 @@ export const sendEmail = async <TEmail extends BaseEmailDocument = BaseEmailDocu
throw new Error('Failed to create email: invalid response from database') throw new Error('Failed to create email: invalid response from database')
} }
// Create an individual job for this email // If processImmediately is true, get the job from the relationship and process it now
const queueName = options.queue || mailingConfig.queue || 'default' if (options.processImmediately) {
if (!payload.jobs) {
if (!payload.jobs) {
if (options.processImmediately) {
throw new Error('PayloadCMS jobs not configured - cannot process email immediately') throw new Error('PayloadCMS jobs not configured - cannot process email immediately')
} else {
console.warn('PayloadCMS jobs not configured - emails will not be processed automatically')
return email as TEmail
} }
}
let jobId: string // Wait a bit for hooks to complete and populate the job relationship
try { // This is necessary because hooks might run asynchronously
const job = await payload.jobs.queue({ await new Promise(resolve => setTimeout(resolve, 100))
queue: queueName,
task: 'process-email', // Refetch the email to get the populated jobs relationship
input: { const emailWithJobs = await payload.findByID({
emailId: String(email.id) collection: collectionSlug,
}, id: email.id,
// If scheduled, set the waitUntil date
waitUntil: emailData.scheduledAt ? new Date(emailData.scheduledAt) : undefined
}) })
jobId = String(job.id) if (!emailWithJobs.jobs || emailWithJobs.jobs.length === 0) {
} catch (error) { throw new Error(`No processing job found for email ${email.id}. The auto-scheduling may have failed.`)
// Clean up the orphaned email since job creation failed
try {
await payload.delete({
collection: collectionSlug,
id: email.id
})
} catch (deleteError) {
console.error(`Failed to clean up orphaned email ${email.id} after job creation failure:`, deleteError)
} }
// Throw the original job creation error // Get the first job ID (should only be one for a new email)
const errorMsg = `Failed to create processing job for email ${email.id}: ${String(error)}` const jobId = Array.isArray(emailWithJobs.jobs)
throw new Error(errorMsg) ? String(emailWithJobs.jobs[0])
} : String(emailWithJobs.jobs)
// If processImmediately is true, process the job now
if (options.processImmediately) {
try { try {
await processJobById(payload, jobId) await processJobById(payload, jobId)
} catch (error) { } catch (error) {
// For immediate processing failures, we could consider cleanup, but the job exists and could be retried later
// So we'll leave the email and job in place for potential retry
throw new Error(`Failed to process email ${email.id} immediately: ${String(error)}`) throw new Error(`Failed to process email ${email.id} immediately: ${String(error)}`)
} }
} }