Files
suaveplan/packages/api-stack/api/api-plugin-campaigns/src/routes/campaigns/action-routes.md
2026-05-27 23:29:53 +01:00

3.6 KiB
Raw Blame History

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, clears sentAt/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) when CampaignStateMachine.transition rejects 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(): PluginRouteEntry
  • buildPauseCampaignRoute(): PluginRouteEntry
  • buildCancelCampaignRoute(): PluginRouteEntry
  • buildDuplicateCampaignRoute(): PluginRouteEntry
  • buildTestSendRoute(): PluginRouteEntry
  • buildCampaignStatsRoute(): PluginRouteEntry
  • buildCampaignEventsRoute(): PluginRouteEntry
  • TestSendSchema, 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.