broadcast-ruby 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 361d356c1fde238ba1734363607796b005a839f72b70885e52c2ed0eaa2b4209
4
- data.tar.gz: 9a1d12cc2b3680689951e325be2100eb64ed9f2ca021c3d69421f336099dec6d
3
+ metadata.gz: 7866007085ed5d19620669069bdfeb9e7babaad5d08b2198d76e19620ce68bde
4
+ data.tar.gz: '082d0663d0a5a2501f445f0f7152e6b6a56467d1e0a61d0ae96e45b3137380d0'
5
5
  SHA512:
6
- metadata.gz: 128a65f8767c17b979e98b8516f7c31a99703449487f6fd7cefe93c9940a1c3cb5c4ae21a5144a7236769ec5f41072626694e169ec2eea041f3c1327265c834d
7
- data.tar.gz: e54a4fa7d5f1e96f9fbc26a8932c05067a6eda7575ee21f3762cc261b0973d42219536ed1650872746ce2854fa449de9ebc3c0192445ab705ceec0933e36df1d
6
+ metadata.gz: bd9d8c35c4bdcea360c59609be5e366bcd066084328db6380e7f3995cc49e7121937b053c6e09c36d94262d54cfc4d4bb978dcae93a58410407a3da78a6d9182
7
+ data.tar.gz: c4d0c47df97790a337dc35b882fea9254c1ae8f41d19784f71bebecb253c6b00740356be0cb5b1b7edf2d2c9965f84556f1fcc31fc350dd1e6214adda167fc63
data/CHANGELOG.md CHANGED
@@ -2,6 +2,152 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [Unreleased]
6
+
7
+ ## [0.4.0] - 2026-08-14
8
+
9
+ ### Fixed — ActionMailer delivery
10
+
11
+ Both reproduced from a real delivered message, and both traced to the same
12
+ ceiling: `Client#send_email` accepted only `to`/`subject`/`body`/`reply_to`, so
13
+ `DeliveryMethod` had no way to describe what it was sending.
14
+
15
+ - **HTML mail arrived as two nested HTML documents.** `deliver!` sent an HTML
16
+ body without flagging it as HTML, so Broadcast recorded the send as plain text
17
+ and wrapped the payload in its own `<html><body>` shell. `deliver!` now sends
18
+ `html_body: true` when the mail has an `html_part`.
19
+ - **Transactional mail carried a one-click unsubscribe.** A password reset
20
+ arrived with `List-Unsubscribe` and `List-Unsubscribe-Post: One-Click`,
21
+ because `deliver!` could not say the send was transactional and the channel's
22
+ unsubscribe setting applied to it. Clicking it marks the recipient
23
+ unsubscribed, silently dropping them from every sequence and broadcast — from
24
+ a click on a security email. `deliver!` now sends
25
+ `include_unsubscribe_link: false` by default; set
26
+ `include_unsubscribe_link: true` in `broadcast_settings` to opt back in. The
27
+ option is consumed by `DeliveryMethod` rather than forwarded, since
28
+ `Configuration` has no such attribute.
29
+
30
+ `Client#send_email` gained matching optional `html_body:` and
31
+ `include_unsubscribe_link:` keywords. Both are omitted from the payload when nil,
32
+ so direct callers of `send_email` are unaffected.
33
+
34
+ **Upgrade note.** ActionMailer deliveries change shape, not just API surface.
35
+ Mail that previously went out flagged as plain text is now flagged as HTML, and
36
+ mail that previously carried `List-Unsubscribe` no longer does. Both are the
37
+ point of the fix, but an app that relied on the unsubscribe footer appearing on
38
+ ActionMailer sends should set `include_unsubscribe_link: true` in
39
+ `broadcast_settings` to keep it.
40
+
41
+ ### Documentation
42
+
43
+ The entries below are documentation only; the behaviour changes in this release
44
+ are the ActionMailer fixes above.
45
+
46
+ - **Autopilot is documented in the README.** 0.3.0 shipped the `Autopilots`
47
+ resource with ten endpoints and no README section, so the only user-facing
48
+ description of it was the source. Adds lifecycle, runs, the write-only
49
+ `openrouter_api_key` guard, and the `autopilot_read` / `autopilot_write` row
50
+ in the permissions table.
51
+ - **`SDK-COVERAGE.md` contradicted itself on Autopilot** — listed at 10/10 in
52
+ the endpoint map and simultaneously under "Not in the API at all" with "zero
53
+ autopilot routes". The latter was true when written and is now removed; the
54
+ section explains what closed it, and records that autopilot sources and tone
55
+ samples remain web-only.
56
+ - Corrected the API version header (2.19.0 → **2.20.0**) and the spec size in
57
+ `SDK-TODO.md` (68 paths / 94 operations → **75 / 104**).
58
+ - Marked `openapi:check` in CI as done — it has been running in the self-hosted
59
+ matrix leg but was still listed as pending.
60
+ - Documented how a **demo instance** answers: the migration API returns 403 for
61
+ every request including valid tokens, and transactional sends are accepted
62
+ but never delivered.
63
+
64
+ ## [0.3.0] - 2026-07-26
65
+
66
+ Catches the gem up to the Broadcast v2.19 API. The API gained a response-warnings
67
+ contract, idempotency keys, and several discovery endpoints since 0.2.0; none of
68
+ them were represented here.
69
+
70
+ ### Breaking
71
+ - **`host` is now required.** It previously defaulted to `https://sendbroadcast.com`,
72
+ which 301-redirects to `sendbroadcast.net` — and since the client did not follow
73
+ redirects, the default failed every request with
74
+ `APIError: Unexpected response: 301`. Broadcast is self-hosted-first, so there is
75
+ no URL the gem can guess. Pass `host:` explicitly or set `BROADCAST_HOST`.
76
+ A host without a scheme is now rejected at construction rather than at request time.
77
+
78
+ ### Added
79
+ - **`client.autopilots`** — the Autopilot API (AI-generated newsletters): CRUD,
80
+ activate / pause / deactivate, trigger_run, and runs. The `autopilot_read` /
81
+ `autopilot_write` token permissions existed since 2026-01-30 but had no
82
+ endpoints behind them until Broadcast v2.19.1. `update` strips a bullet-masked
83
+ `openrouter_api_key` so a fetch-modify-save cannot destroy the stored key.
84
+ - **API warnings.** Successful writes can carry a `warnings` array describing what
85
+ the server ignored (`unrecognized_parameter`, `parameter_ignored`,
86
+ `parameter_overridden`, `double_opt_in_skipped`). Exposed as `result.warnings`
87
+ and controlled by `warnings_mode:` — `:log` (default), `:raise`, or `:ignore`.
88
+ - **`Broadcast::Response`.** All JSON calls now return a Hash subclass carrying
89
+ `#warnings`, `#rate_limit`, `#status`, `#headers`, and `#idempotent_replay?`.
90
+ Existing Hash-based code is unaffected.
91
+ - **Idempotent transactional sends.** `transactionals.create(..., idempotency_key:)`
92
+ sends the `Idempotency-Key` header; `Broadcast::ConflictError` maps 409 (previously
93
+ a generic `APIError`).
94
+ - **Rate-limit awareness.** `result.rate_limit` exposes the `X-RateLimit-*` headers,
95
+ `RateLimitError#retry_after` exposes `Retry-After`, and 429s are now retried
96
+ honouring it, bounded by the new `max_retry_delay` setting (default 30s).
97
+ - **Discovery endpoints:** `client.whoami`, `client.status`, `client.prime`, and
98
+ `client.skill` (plain text).
99
+ - **Migration/export namespace:** `client.migration.*` covers all 19 read-only
100
+ endpoints under `/api/migration/v1`, plus `each_record` for automatic paging and
101
+ `download_file_asset` for binary assets. Requires an admin token.
102
+ - **`Broadcast::Webhook::EVENT_TYPES`** and per-category constants (`EMAIL_EVENTS`,
103
+ `SUBSCRIBER_EVENTS`, `BROADCAST_EVENTS`, `SEQUENCE_EVENTS`, `SYSTEM_EVENTS`).
104
+ - `BROADCAST_HOST` / `BROADCAST_API_TOKEN` environment fallbacks, matching the
105
+ Broadcast CLI's config keys.
106
+ - Redirect handling: same-host GETs follow up to 3 hops. Writes, and any redirect
107
+ that changes host, fail with an error naming the target — every request carries
108
+ the bearer token, so following a cross-host redirect would leak it.
109
+ - Documented attributes added to the API after 0.2.0 — template
110
+ `template_purpose` / `confirmation_text` / `default_confirmation` /
111
+ `confirmation_page_settings`; opt-in form `confirmation_email_template_id` /
112
+ `welcome_email_template_id` / `confirmation_redirect_url` /
113
+ `include_unsubscribe_link_in_confirmation`; admin-only `confirmed_at` on
114
+ subscriber create; and the full subscriber list filter set.
115
+
116
+ ### Fixed
117
+ - Debug logging redacted: SMTP passwords and provider API keys are no longer written
118
+ to the log when `debug: true`.
119
+ - 2xx responses other than 200/201 (e.g. 204) no longer raise.
120
+ - A 2xx response with a non-JSON body no longer raises `JSON::ParserError`.
121
+ - Error messages now include ActiveModel-style `errors` hashes, not just `error` strings.
122
+ - Binary responses keep binary encoding instead of being tagged UTF-8.
123
+ - The ActionMailer delivery method no longer wraps `WarningError` in `DeliveryError` —
124
+ the email was sent, so reporting a delivery failure would be wrong.
125
+ - **Packaging:** the gemspec shipped this repo's internal planning documents
126
+ (`TODO.md`, `SDK-TODO.md`, `.api-coverage.yml`) and the committed `.gem`
127
+ artifacts at the repo root to every install. The file list now excludes them,
128
+ along with `.github/` and `pkg/`. `SDK-COVERAGE.md` is kept deliberately —
129
+ "what does this gem support" is a user's question.
130
+
131
+ ### Internal
132
+ - HTTP transport extracted from `Client` into `Broadcast::Connection`, and debug
133
+ logging into `Broadcast::DebugLogger`.
134
+ - Test coverage for the gemspec's file list, since a wrong package fails
135
+ silently: nothing breaks, the download is just wrong.
136
+
137
+ ### Verification
138
+ - 292 tests, rubocop clean.
139
+ - Coverage against the generated OpenAPI document: 104/104 operations
140
+ (`rake "openapi:coverage[../broadcast-ruby]"` in the `broadcast` repo).
141
+ - The built `.gem` was installed into an isolated `GEM_HOME` and exercised.
142
+ - `rake test_live` run against a real self-hosted instance: 14 tests, 52
143
+ assertions, 0 failures. Covers the discovery endpoints, the `X-RateLimit-*`
144
+ headers, and full create/read/update/delete round-trips for subscribers,
145
+ sequences, segments, templates, and webhook endpoints.
146
+ - Two gaps in that run, both deliberate: the transactional **send** test is
147
+ skipped unless `BROADCAST_TEST_EMAIL` is set, so no mail was delivered; and
148
+ the target was a local development instance over plain HTTP, so TLS and
149
+ real-world latency and rate limiting are still unexercised.
150
+
5
151
  ## [0.2.0] - 2026-04-28
6
152
 
7
153
  ### Added
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- broadcast-ruby (0.2.0)
4
+ broadcast-ruby (0.4.0)
5
5
  base64
6
6
 
7
7
  GEM
@@ -155,6 +155,7 @@ GEM
155
155
 
156
156
  PLATFORMS
157
157
  arm64-darwin-24
158
+ arm64-darwin-25
158
159
  x86_64-linux
159
160
 
160
161
  DEPENDENCIES
@@ -176,7 +177,7 @@ CHECKSUMS
176
177
  ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
177
178
  base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
178
179
  bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7
179
- broadcast-ruby (0.2.0)
180
+ broadcast-ruby (0.4.0)
180
181
  builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
181
182
  concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
182
183
  connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a