3.6 KiB
Campaign action routes
Purpose
This module owns the campaign action routes — state-machine
transitions (/send, /pause, /cancel), the /duplicate
shallow-copy endpoint, the /test ad-hoc send, and the two
delivery-tracking-backed read endpoints (/stats and /events).
State transitions are authoritative: invalid transitions raise
CAMPAIGNS_API.INVALID_STATE_TRANSITION mapped to HTTP 409 so clients
can drive UI affordances (enable/disable a "Pause" button) off a
stable code rather than parsing error messages.
The action routes consume the in-domain CampaignStateMachine from
@suaveplan/campaigns. Every transition decision flows through that
single source of truth — no string-comparisons of status are
duplicated at the route layer, eliminating drift between the engine
and the HTTP surface.
Features
- 3 transition routes:
buildSendCampaignRoute,buildPauseCampaignRoute,buildCancelCampaignRoute. - 1 clone route:
buildDuplicateCampaignRoute— copies metadata and audience/template config, resets stats, clearssentAt/completedAt. - 1 test-send route:
buildTestSendRoute— validates a recipient list of 1–10 email addresses and echoes them back. - 2 reporting routes consuming
DeliveryEventStore:buildCampaignStatsRoute,buildCampaignEventsRoute. - Transition guard raises
INVALID_STATE_TRANSITION(HTTP 409) whenCampaignStateMachine.transitionrejects the (status, event) pair.
Usage: Basic
import { buildSendCampaignRoute } from "@suaveplan/api-plugin-campaigns";
const entry = buildSendCampaignRoute();
app.post(entry.route.path, (c) => entry.handler(c));
Usage: Advanced
import {
buildCampaignEventsRoute,
buildCampaignStatsRoute,
buildCancelCampaignRoute,
buildDuplicateCampaignRoute,
buildPauseCampaignRoute,
buildSendCampaignRoute,
buildTestSendRoute,
} from "@suaveplan/api-plugin-campaigns";
const actionEntries = [
buildSendCampaignRoute(),
buildPauseCampaignRoute(),
buildCancelCampaignRoute(),
buildDuplicateCampaignRoute(),
buildTestSendRoute(),
buildCampaignStatsRoute(),
buildCampaignEventsRoute(),
];
API Reference
buildSendCampaignRoute(): PluginRouteEntrybuildPauseCampaignRoute(): PluginRouteEntrybuildCancelCampaignRoute(): PluginRouteEntrybuildDuplicateCampaignRoute(): PluginRouteEntrybuildTestSendRoute(): PluginRouteEntrybuildCampaignStatsRoute(): PluginRouteEntrybuildCampaignEventsRoute(): PluginRouteEntryTestSendSchema,EventQuerySchema,DeliveryStatsResponseSchema— Zod schemas re-exported for documentation generators and tests.
Implementation Notes
applyTransition wraps CampaignStateMachine.transition in a
CampaignsApiError so a failed transition surfaces as
CAMPAIGNS_API.INVALID_STATE_TRANSITION rather than the
domain-package's CampaignsError (which carries an unrelated
namespace). Wrapping at this seam keeps the route layer's error
vocabulary self-contained: every error a client sees from the
campaigns plugin carries a CAMPAIGNS_API.* code.
The /duplicate handler intentionally copies all configuration
fields (template, audience, throttle, abTestConfig) but resets the
volatile fields (id, status, stats, sentAt, completedAt,
createdAt, updatedAt). The (copy) suffix is appended to the
name so the duplicate is visually distinct in listing UIs without
forcing the operator to provide a new name up front. The route
handlers' OTel attributes record route plus outcome so an
operator can chart per-route success/error counters without parsing
log messages.