first commit
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
# 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
|
||||
|
||||
```ts
|
||||
import { buildSendCampaignRoute } from "@suaveplan/api-plugin-campaigns";
|
||||
|
||||
const entry = buildSendCampaignRoute();
|
||||
app.post(entry.route.path, (c) => entry.handler(c));
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
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.
|
||||
Reference in New Issue
Block a user