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 +4 -4
- data/CHANGELOG.md +146 -0
- data/Gemfile.lock +3 -2
- data/README.md +434 -22
- data/SDK-COVERAGE.md +469 -0
- data/lib/broadcast/client.rb +65 -138
- data/lib/broadcast/configuration.rb +53 -4
- data/lib/broadcast/connection.rb +278 -0
- data/lib/broadcast/debug_logger.rb +64 -0
- data/lib/broadcast/delivery_method.rb +22 -2
- data/lib/broadcast/errors.rb +27 -1
- data/lib/broadcast/resources/autopilots.rb +100 -0
- data/lib/broadcast/resources/discovery.rb +36 -0
- data/lib/broadcast/resources/global_suppressions.rb +43 -0
- data/lib/broadcast/resources/migration.rb +75 -0
- data/lib/broadcast/resources/opt_in_forms.rb +12 -0
- data/lib/broadcast/resources/subscribers.rb +24 -0
- data/lib/broadcast/resources/suppressions.rb +56 -0
- data/lib/broadcast/resources/templates.rb +17 -0
- data/lib/broadcast/resources/transactionals.rb +39 -2
- data/lib/broadcast/response.rb +104 -0
- data/lib/broadcast/version.rb +1 -1
- data/lib/broadcast/webhook.rb +34 -0
- data/lib/broadcast.rb +8 -0
- metadata +11 -3
- data/.rubocop.yml +0 -50
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7866007085ed5d19620669069bdfeb9e7babaad5d08b2198d76e19620ce68bde
|
|
4
|
+
data.tar.gz: '082d0663d0a5a2501f445f0f7152e6b6a56467d1e0a61d0ae96e45b3137380d0'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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.
|
|
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
|