Web actions:
* resumeReminderRunAction({ runId }) → validates ownership and that
the run is in 'paused' state, then publishes a reminder.resume
command via pg_notify('bot.command'). The bot's command-consumer
picks it up and enqueues a fresh pg-boss job at REMINDER_FIRE_QUEUE
carrying { reminderId, runId }; fire-reminder's existing resume
branch attaches to the row.
* cancelReminderRunAction({ runId }) → flips remaining 'pending'
targets to 'skipped' with error="canceled by operator", marks the
run 'partial' with a clear errorSummary, and lifts the parent
reminder out of 'paused' (recurring → active so the next
occurrence fires; one-off → ended).
Bot:
* New BotCommand variant { type: "reminder.resume"; reminderId; runId }
* command-consumer registers handleResumeReminder which calls
enqueueReminderResume(boss, reminderId, runId) — a sibling of
scheduleReminderFire that posts the job at REMINDER_FIRE_QUEUE
with { reminderId, runId } and singletonKey "reminder:resume:<runId>"
so the resume doesn't conflict with a future-occurrence schedule.
Tests:
* reminders.run-actions.test.ts (11 tests) — every guard rail
(invalid uuid, missing run, missing reminder, foreign operator,
wrong status) and the recurring/one-off lifecycle branches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
17 lines
509 B
TypeScript
17 lines
509 B
TypeScript
import { getBoss } from "../scheduler/pgboss-client.js";
|
|
import {
|
|
scheduleReminderFire,
|
|
enqueueReminderResume,
|
|
} from "../scheduler/reminder-jobs.js";
|
|
|
|
export async function handleScheduleReminder(reminderId: string, scheduledAtIso: string): Promise<void> {
|
|
await scheduleReminderFire(getBoss(), reminderId, new Date(scheduledAtIso));
|
|
}
|
|
|
|
export async function handleResumeReminder(
|
|
reminderId: string,
|
|
runId: string,
|
|
): Promise<void> {
|
|
await enqueueReminderResume(getBoss(), reminderId, runId);
|
|
}
|