feat: add wrangler email routing commands#12932
feat: add wrangler email routing commands#12932thomasgauvin wants to merge 34 commits intocloudflare:mainfrom
Conversation
🦋 Changeset detectedLatest commit: 0abedff The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
LuisDuarte1
left a comment
There was a problem hiding this comment.
from the EMAIL team:
├── rules list/get/create/update/delete # Routing rules CRUD
├── rules catch-all get/update # Catch-all rule
why do we have to have 2 seperate comands for rules that are not catch-alls and rules that are a catch-all? couldn't rules * also handle this?
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
Add CLI commands wrapping the Cloudflare Email Routing REST API: - wrangler email routing list - list zones with email routing status - wrangler email routing settings/enable/disable - manage zone settings - wrangler email routing dns get/unlock - DNS record management - wrangler email routing rules list/get/create/update/delete - routing rules CRUD - wrangler email routing rules catch-all get/update - catch-all rule management - wrangler email routing addresses list/get/create/delete - destination addresses Zone-scoped commands support --zone (domain) and --zone-id flags. Address commands are account-scoped. Also adds email_routing:write OAuth scope to DefaultScopes so wrangler login grants the necessary permissions.
- Merge catch-all into rules commands (rules get/update catch-all) - Remove separate catch-all sub-namespace per EMAIL team feedback - Add email sending control plane: subdomains list/get/create/delete, dns get - Add email sending data plane: send (builder) and send-raw (MIME) - Add 19 new tests for email sending commands (52 total) - Update changeset to include sending commands
ee37d72 to
135bbce
Compare
create-cloudflare
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-shared
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-pool-workers
@cloudflare/workers-editor-shared
wrangler
commit: |
- Rename owner from 'Product: Email Routing' to 'Product: Email Service' per EMAIL team feedback
- Use path.basename() instead of split('/') for cross-platform filename extraction
- Use fetchPagedListResult for listEmailSendingSubdomains to handle pagination
- Replace non-null assertion in rules/update.ts with type narrowing guard
- Replace non-null assertion in send-raw.ts with ?? fallback
- Use node: prefix for fs imports per repo conventions
The email sending commands use a separate OAuth scope (email_sending:write) from the routing commands (email_routing:write). Without this scope, wrangler login tokens get a 10000 auth error when calling email sending API endpoints. Bach staging MR: !4176 (merged)
- Change "open-beta" to "open beta" (correct status literal type)
- Fix ComplianceConfig import to use @cloudflare/workers-utils instead of
non-existent ../environment-variables/misc-variables module
- Import describe/it/beforeEach/afterEach/vi from vitest in test file
- Use ({ expect }) => test context pattern for all it() callbacks
per repo conventions (no global expect)
- Cast request.json() as Record<string, unknown> to fix spread type errors
- Use /accounts/{accountId}/email/routing/zones instead of fetching
/zones then N separate /zones/{id}/email/routing calls. This matches
the dashboard behavior and is more efficient (1 API call vs N+1).
- Add order/direction query params to rules list and addresses list
to match dashboard API calls.
- Add VS Code debug launch configs for email routing commands.
- Enable: use POST /zones/{id}/email/routing/enable (was POST /dns)
- Disable: use POST /zones/{id}/email/routing/disable (was DELETE /dns)
- Unlock: use POST /zones/{id}/email/routing/unlock (was PATCH /dns)
- Update tests to mock the correct endpoints
- Add mockListEmailRoutingZones helper for list tests
- Replace "not configured" and "error" per-zone tests with "disabled" test
matching the single-endpoint list implementation
- Align with Stratus dashboard API calls per code review
- Guard against undefined response.errors in throwFetchError (fixes crash
on APIs returning {code, error} instead of {errors: [...]})
- Handle messages as objects, not just strings (fixes esbuild formatMessages
crash when API returns {code, message} objects in messages array)
- Surface non-standard error details (e.g. 'Unauthorized [code: 2036]')
when standard errors array is empty
- Fix dns-unlock showing 'status: undefined' by using 'enabled' field
which the PATCH /email/routing/dns endpoint actually returns
|
Codeowners approval required for this PR:
Show detailed file reviewers |
- Switch DNS record display from table to vertical format (DKIM keys made tables unreadable) - Remove empty status column from zone list (API doesn't return it) - Separate catch-all from regular rules in rules list with usage hint - Fallback to catch-all endpoint when rules get receives error 2020
When the API returns empty delivered/queued/bounced arrays, the commands previously printed nothing. Now shows 'Email sent successfully.' as feedback. Also adds emoji prefixes to delivery status output.
- Remove dead listZones function and CloudflareZone type - Remove unnecessary JSDoc comments in utils.ts - Remove section separator comments from client.ts and index.ts - Remove redundant null guard in rules/update.ts (already in validateArgs) - Deduplicate EmailRoutingCatchAllAction/Matcher types (reuse base types) - Extract logSendResult helper to avoid duplication in send/send-raw
Add zone-level email sending management commands:
- wrangler email sending settings --zone <domain>
- wrangler email sending enable --zone <domain>
- wrangler email sending disable --zone <domain>
These mirror the email routing enable/disable pattern and use the
/zones/{id}/email/sending, /enable, and /disable API endpoints.
Add wrangler email sending list to show all zones with email sending
status, mirroring wrangler email routing list. Uses the
/accounts/{id}/email/sending/zones API endpoint.
…ng DNS endpoints
The documented email routing enable/disable endpoints (POST/DELETE
/zones/{id}/email/routing/dns) require Zone Settings Write permission.
Add zone:write to DefaultScopes so wrangler login requests it.
…il routing DNS endpoints" This reverts commit 92b27d5.
|
Docs will be done in here: cloudflare/cloudflare-docs#27805 |
…s namespace Replace the nested subdomains CRUD with flat domain-aware commands: - `wrangler email sending enable <domain>` — auto-detects zone vs subdomain - `wrangler email sending disable <domain>` — same - `wrangler email sending settings <domain>` — shows zone + subdomains - `wrangler email sending dns get <domain>` — resolves subdomain tag automatically The CLI walks up domain labels to find the zone (e.g. sub.example.com tries sub.example.com, then example.com), so users never need to specify --zone separately. Removes subdomains/ directory, EmailSendingSubdomain type, and 4 subdomain CRUD client functions.
- Add confirmation prompts to destructive commands (disable, dns-unlock, rules delete, addresses delete) with --force/-y bypass - Add EmailSendingSettings type to replace unsafe casts for subdomains - Fix catch-all fallback in rules get to match both tag and id - Fix rules update to include --name in catch-all payload - Fix rules list contradictory output when only catch-all exists - Fix sending dns-get to handle zone-level domains, not just subdomains - Remove dead code (mockListZones, null guard in sending/disable) - Remove emojis from CLI output - Clean up test mock signatures and formatting nits
- Reject empty header names in --header parsing (e.g. ':value') - Extract shared logSendResult() into sending/utils.ts to deduplicate inline emoji-laden logging in send.ts and send-raw.ts - Add tests for malformed header input - Remove emojis from send result output
…le, zone-level DNS - Add optional --zone-id flag to all sending commands (enable, disable, settings, dns get) to skip zone lookup for tokens without zone:read - Add confirmation prompt to email sending disable (matches routing disable) - Add zone-level /email/sending/dns endpoint for apex domain DNS records, use subdomain endpoint only for actual subdomains - Update resolveDomain() to accept optional zoneId override
Replace --zone/--zone-id flags with a positional domain argument for all email routing zone-scoped commands, matching the pattern already used by email sending commands. Before: wrangler email routing settings --zone example.com After: wrangler email routing settings example.com --zone-id is kept as an optional flag to skip zone lookup. Rules commands with rule-id use two positionals: domain then rule-id (e.g. wrangler email routing rules get example.com rule-id-1).
Add 17 new tests covering: - --force flag for disable, dns-unlock, rules delete - User declining confirmation for disable, dns-unlock, rules delete - --mime-file and missing file error for send-raw - --attachment and missing file error for send - Error 2020 catch-all fallback for rules get - Catch-all rule in rules list output - Empty DNS records for routing dns get - Help text for email routing dns namespace - Sending list (happy + empty) and settings commands Also removes spurious status column assertion from routing list test (enabled yes/no already conveys the same info).
…main
When --zone-id is provided, resolveDomain previously used labels.slice(-2)
to guess the zone name, which broke multi-label TLDs like .co.uk, .com.br.
For example, example.co.uk was misclassified as a subdomain of co.uk.
Now fetches GET /zones/{zoneId} to get the actual zone name, so subdomain
detection is correct for all TLDs.
Adds 7 tests covering domain + --zone-id combinations:
- Zone-level with --zone-id (body has no name)
- Subdomain with --zone-id (body has name)
- Multi-label TLD zone-level with --zone-id (example.co.uk)
- Multi-label TLD subdomain with --zone-id (notifications.example.co.uk)
- Disable zone-level and subdomain with --zone-id
- DNS get zone-level and subdomain paths with --zone-id
|
Manually reviewed all files. Mostly straightforward CRUD with approvals, some lookups of domains -> zones |
|
Tested manually, summary: routing list PASS |
ascorbic
left a comment
There was a problem hiding this comment.
Approving with one small comment
workers-devprod
left a comment
There was a problem hiding this comment.
Codeowners reviews satisfied
Summary
Adds
wrangler email routingandwrangler email sendingcommands wrapping the Cloudflare Email Routing and Email Sending REST APIs.Commands
Key design decisions
<domain>as a positional arg (e.g.wrangler email routing settings example.com), with optional--zone-idto skip zone lookupresolveDomainwalks up domain labels to find the zone; when--zone-idis provided, fetchesGET /zones/{id}to correctly handle multi-label TLDs (.co.uk,.com.br)createCommand(),fetchResult()/fetchPagedListResult()email_routing:writeandemail_sending:writetoDefaultScopes. Bach MRs: staging / productionTesting
Documentation
PR: cloudflare/cloudflare-docs#27805