broadcast-ruby 0.5.0 → 0.7.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 +24 -0
- data/Gemfile.lock +2 -2
- data/README.md +39 -3
- data/SDK-COVERAGE.md +3 -2
- data/lib/broadcast/client.rb +4 -0
- data/lib/broadcast/resources/migration.rb +2 -2
- data/lib/broadcast/resources/subscribers.rb +6 -2
- data/lib/broadcast/resources/topics.rb +31 -0
- data/lib/broadcast/version.rb +1 -1
- data/lib/broadcast/webhook.rb +1 -1
- data/lib/broadcast.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6691d4bb7e0fb74c2f3fe326d1ef2ae6797ec957f730be3d62287c07b13b2b65
|
|
4
|
+
data.tar.gz: 6117bc0c528ddb576ec51eafe1c56071b05fc354d06cbe66b067effbe2f8e560
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 86f1f4579ff54f8677f1848efa21d097bea66ab5e2102fcc05763021a4f7ff05b34818b93e506631f2cd241bbe87c21d587bc0f6ff60f72adadad4f3acefc009
|
|
7
|
+
data.tar.gz: b0a6d3889b396bce312a174f3da12e87a3f20ac729c91c26891a03f1b6f7f1f696f02d1c5681773b1f3fdc649034426d52a637891fe3153b8e0d0e4b044fc6a1
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.7.0] - 2026-10-06
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `Topics` resource (`client.topics`): `list`, `get_topic`, `create`, `update`,
|
|
12
|
+
`delete` for `/api/v1/topics` (subscriber topics; uses the subscriber
|
|
13
|
+
permissions). Broadcasts and sequences accept `topic_id`.
|
|
14
|
+
- `Subscribers#update` takes `custom_data_mode:` (`'replace'` default, or
|
|
15
|
+
`'merge'`: change only the keys sent, `nil` deletes a key).
|
|
16
|
+
- `subscriber.preferences_updated` webhook event type, in
|
|
17
|
+
`Broadcast::Webhook::SUBSCRIBER_EVENTS` and `EVENT_TYPES` (now 35).
|
|
18
|
+
|
|
19
|
+
## [0.6.0] - 2026-10-03
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- `Migration#unsubscribed_emails` for `GET /api/migration/v1/unsubscribed_emails`,
|
|
24
|
+
the channel's own suppression list (rows `id`, `email`,
|
|
25
|
+
`broadcast_channel_id`, `created_at`, `updated_at`). `each_record(:unsubscribed_emails)`
|
|
26
|
+
pages it. `suppressions` returns only the global suppression list, so an
|
|
27
|
+
export needs both.
|
|
28
|
+
|
|
5
29
|
## [0.5.0] - 2026-09-25
|
|
6
30
|
|
|
7
31
|
### 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.7.0)
|
|
5
5
|
base64
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -177,7 +177,7 @@ CHECKSUMS
|
|
|
177
177
|
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
178
178
|
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
179
179
|
bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7
|
|
180
|
-
broadcast-ruby (0.
|
|
180
|
+
broadcast-ruby (0.7.0)
|
|
181
181
|
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
|
|
182
182
|
concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
|
|
183
183
|
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
|
data/README.md
CHANGED
|
@@ -712,6 +712,38 @@ client.broadcasts.statistics_links(1, sort: 'clicks', order: 'desc')
|
|
|
712
712
|
|
|
713
713
|
---
|
|
714
714
|
|
|
715
|
+
## Topics
|
|
716
|
+
|
|
717
|
+
A topic is one kind of email subscribers opt in to or out of (webinars, offers).
|
|
718
|
+
Its value lives in a top-level `custom_data` key (`true`, `false`, or no value)
|
|
719
|
+
or in a tag. `unset_receives` decides, at send time, whether a subscriber with no
|
|
720
|
+
value receives it; Broadcast never writes a default. A broadcast or sequence with
|
|
721
|
+
a `topic_id` reaches only the topic's receivers: (segments) AND topic.
|
|
722
|
+
|
|
723
|
+
**Required permissions:** `subscribers_read`, `subscribers_write`
|
|
724
|
+
|
|
725
|
+
```ruby
|
|
726
|
+
client.topics.list['data'] # => [{'id' => 1, 'name' => 'Webinars', 'custom_data_key' => 'sub_webinars', ...}]
|
|
727
|
+
client.topics.get_topic(1)
|
|
728
|
+
|
|
729
|
+
topic = client.topics.create(name: 'Webinars', custom_data_key: 'sub_webinars')
|
|
730
|
+
client.topics.create(name: 'News', storage: 'tag', tag_name: 'news')
|
|
731
|
+
client.topics.update(topic['id'], unset_receives: false)
|
|
732
|
+
client.topics.delete(topic['id']) # 422 while a broadcast or sequence uses it
|
|
733
|
+
|
|
734
|
+
# Send to a topic
|
|
735
|
+
client.broadcasts.create(subject: 'Webinar', body: '...', segment_ids: [2], topic_id: topic['id'])
|
|
736
|
+
|
|
737
|
+
# Change only the keys you send (nil deletes a key); the default replaces custom_data
|
|
738
|
+
client.subscribers.update('jane@example.com', custom_data: { sub_webinars: false }, custom_data_mode: 'merge')
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
The subscriber JSON has `topics`: each topic's stored value (`true`, `false`, or
|
|
742
|
+
`nil`). Topic changes fire `subscriber.preferences_updated` (from/to per topic,
|
|
743
|
+
`changed_at`, `change_source`) as well as `subscriber.updated`.
|
|
744
|
+
|
|
745
|
+
---
|
|
746
|
+
|
|
715
747
|
## Segments
|
|
716
748
|
|
|
717
749
|
Define subscriber groups using rules for targeted broadcasts and sequence enrollment.
|
|
@@ -1131,7 +1163,7 @@ secret = result['secret']
|
|
|
1131
1163
|
|
|
1132
1164
|
# Every valid event type is available as a constant. An unknown event type is
|
|
1133
1165
|
# dropped server-side rather than rejected, so subscribe from these.
|
|
1134
|
-
Broadcast::Webhook::EVENT_TYPES # all
|
|
1166
|
+
Broadcast::Webhook::EVENT_TYPES # all 35
|
|
1135
1167
|
Broadcast::Webhook::EMAIL_EVENTS # email.sent, email.delivered, ...
|
|
1136
1168
|
Broadcast::Webhook::SUBSCRIBER_EVENTS # subscriber.created, ..., subscribers.purged
|
|
1137
1169
|
Broadcast::Webhook::BROADCAST_EVENTS # broadcast.sending, broadcast.sent, ...
|
|
@@ -1380,10 +1412,14 @@ Available collections:
|
|
|
1380
1412
|
```
|
|
1381
1413
|
channels subscribers templates segments sequences email_servers
|
|
1382
1414
|
opt_in_forms broadcasts outbound_receipts webhook_endpoints tokens
|
|
1383
|
-
suppressions tags users link_redirects
|
|
1384
|
-
subscriber_histories file_assets
|
|
1415
|
+
suppressions unsubscribed_emails tags users link_redirects
|
|
1416
|
+
link_clicks subscriber_histories file_assets
|
|
1385
1417
|
```
|
|
1386
1418
|
|
|
1419
|
+
`suppressions` is the global suppression list (GlobalSuppression rows, global
|
|
1420
|
+
or scoped to the channel). `unsubscribed_emails` is the channel's own
|
|
1421
|
+
suppression list. Export both to keep every suppressed address.
|
|
1422
|
+
|
|
1387
1423
|
File assets are downloaded separately and return raw bytes:
|
|
1388
1424
|
|
|
1389
1425
|
```ruby
|
data/SDK-COVERAGE.md
CHANGED
|
@@ -53,7 +53,7 @@ Two details worth knowing when reading the map below:
|
|
|
53
53
|
- **PATCH is canonical.** Rails' `resources` routes `update` to both PATCH and
|
|
54
54
|
PUT. They are one operation; the generator drops the PUT alias. Counting both
|
|
55
55
|
would inflate the denominator by 8 and make this gem look 8 endpoints short.
|
|
56
|
-
- **Metaprogrammed endpoints are declared.** The
|
|
56
|
+
- **Metaprogrammed endpoints are declared.** The 19 migration collections are
|
|
57
57
|
defined by `define_method` over `Migration::COLLECTIONS`, so no path literal
|
|
58
58
|
exists to scan. They are listed in `.api-coverage.yml` and marked
|
|
59
59
|
*(generated)* below. That file is produced from the gem's own constants — it
|
|
@@ -268,6 +268,7 @@ on the client and the gem attaches it.
|
|
|
268
268
|
| GET | `/api/migration/v1/webhook_endpoints` | *(generated)* `Migration#webhook_endpoints` |
|
|
269
269
|
| GET | `/api/migration/v1/tokens` | *(generated)* `Migration#tokens` |
|
|
270
270
|
| GET | `/api/migration/v1/suppressions` | *(generated)* `Migration#suppressions` |
|
|
271
|
+
| GET | `/api/migration/v1/unsubscribed_emails` | *(generated)* `Migration#unsubscribed_emails` |
|
|
271
272
|
| GET | `/api/migration/v1/tags` | *(generated)* `Migration#tags` |
|
|
272
273
|
| GET | `/api/migration/v1/users` | *(generated)* `Migration#users` |
|
|
273
274
|
| GET | `/api/migration/v1/link_redirects` | *(generated)* `Migration#link_redirects` |
|
|
@@ -330,7 +331,7 @@ Inbound webhooks — the app calling you.
|
|
|
330
331
|
| HMAC-SHA256 verification (`v1,<base64>`) | ✅ `Webhook.verify` |
|
|
331
332
|
| 5-minute timestamp tolerance | ✅ Replay protection |
|
|
332
333
|
| Constant-time comparison | ✅ `OpenSSL.fixed_length_secure_compare` |
|
|
333
|
-
| Event type constants | ✅ `Webhook::EVENT_TYPES` —
|
|
334
|
+
| Event type constants | ✅ `Webhook::EVENT_TYPES` — 35 values |
|
|
334
335
|
|
|
335
336
|
Categories: `EMAIL_EVENTS` (8), `SUBSCRIBER_EVENTS` (7), `BROADCAST_EVENTS` (8),
|
|
336
337
|
`SEQUENCE_EVENTS` (7), `SYSTEM_EVENTS` (2).
|
data/lib/broadcast/client.rb
CHANGED
|
@@ -20,8 +20,8 @@ module Broadcast
|
|
|
20
20
|
COLLECTIONS = %i[
|
|
21
21
|
channels subscribers templates segments sequences email_servers
|
|
22
22
|
opt_in_forms broadcasts outbound_receipts webhook_endpoints tokens
|
|
23
|
-
suppressions tags users link_redirects link_clicks
|
|
24
|
-
file_assets
|
|
23
|
+
suppressions unsubscribed_emails tags users link_redirects link_clicks
|
|
24
|
+
subscriber_histories file_assets
|
|
25
25
|
].freeze
|
|
26
26
|
|
|
27
27
|
COLLECTIONS.each do |collection|
|
|
@@ -57,8 +57,12 @@ module Broadcast
|
|
|
57
57
|
post('/api/v1/subscribers.json', payload)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
# custom_data_mode: 'replace' (the API default) stores custom_data as sent;
|
|
61
|
+
# 'merge' changes only the keys sent, and a nil value deletes that key.
|
|
62
|
+
def update(email, custom_data_mode: nil, **attrs)
|
|
63
|
+
body = { email: email, subscriber: attrs }
|
|
64
|
+
body[:custom_data_mode] = custom_data_mode if custom_data_mode
|
|
65
|
+
patch('/api/v1/subscribers.json', body)
|
|
62
66
|
end
|
|
63
67
|
|
|
64
68
|
def add_tags(email, tags)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Broadcast
|
|
4
|
+
module Resources
|
|
5
|
+
# Subscriber topics: kinds of email subscribers opt in to or out of. A topic
|
|
6
|
+
# reads a top-level custom_data key (true, false, or no value) or a tag.
|
|
7
|
+
# Uses the token's subscriber permissions.
|
|
8
|
+
class Topics < Base
|
|
9
|
+
def list(**params)
|
|
10
|
+
get('/api/v1/topics.json', params)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_topic(id)
|
|
14
|
+
get("/api/v1/topics/#{id}.json")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create(**attrs)
|
|
18
|
+
post('/api/v1/topics', { topic: attrs })
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def update(id, **attrs)
|
|
22
|
+
patch("/api/v1/topics/#{id}", { topic: attrs })
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Refused (422) while a broadcast or sequence uses the topic.
|
|
26
|
+
def delete(id)
|
|
27
|
+
@client.request(:delete, "/api/v1/topics/#{id}")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/broadcast/version.rb
CHANGED
data/lib/broadcast/webhook.rb
CHANGED
|
@@ -18,7 +18,7 @@ module Broadcast
|
|
|
18
18
|
# subscribers.* are sent once for a whole-list purge, in place of a
|
|
19
19
|
# subscriber.deleted per row.
|
|
20
20
|
SUBSCRIBER_EVENTS = %w[
|
|
21
|
-
subscriber.created subscriber.updated subscriber.deleted
|
|
21
|
+
subscriber.created subscriber.updated subscriber.preferences_updated subscriber.deleted
|
|
22
22
|
subscriber.subscribed subscriber.unsubscribed subscriber.bounced
|
|
23
23
|
subscriber.complained subscribers.purged subscribers.purge_failed
|
|
24
24
|
].freeze
|
data/lib/broadcast.rb
CHANGED
|
@@ -13,6 +13,7 @@ require_relative 'broadcast/resources/subscribers'
|
|
|
13
13
|
require_relative 'broadcast/resources/sequences'
|
|
14
14
|
require_relative 'broadcast/resources/broadcasts'
|
|
15
15
|
require_relative 'broadcast/resources/segments'
|
|
16
|
+
require_relative 'broadcast/resources/topics'
|
|
16
17
|
require_relative 'broadcast/resources/templates'
|
|
17
18
|
require_relative 'broadcast/resources/webhook_endpoints'
|
|
18
19
|
require_relative 'broadcast/resources/transactionals'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: broadcast-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simon Chiu
|
|
@@ -61,6 +61,7 @@ files:
|
|
|
61
61
|
- lib/broadcast/resources/subscribers.rb
|
|
62
62
|
- lib/broadcast/resources/suppressions.rb
|
|
63
63
|
- lib/broadcast/resources/templates.rb
|
|
64
|
+
- lib/broadcast/resources/topics.rb
|
|
64
65
|
- lib/broadcast/resources/transactionals.rb
|
|
65
66
|
- lib/broadcast/resources/users.rb
|
|
66
67
|
- lib/broadcast/resources/webhook_endpoints.rb
|