broadcast-ruby 0.4.1 → 0.6.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 +33 -0
- data/Gemfile.lock +2 -2
- data/README.md +91 -8
- data/SDK-COVERAGE.md +3 -2
- data/lib/broadcast/client.rb +10 -0
- data/lib/broadcast/connection.rb +1 -0
- data/lib/broadcast/resources/channel.rb +22 -0
- data/lib/broadcast/resources/migration.rb +2 -2
- data/lib/broadcast/resources/users.rb +103 -0
- data/lib/broadcast/version.rb +1 -1
- data/lib/broadcast/webhook.rb +3 -1
- data/lib/broadcast.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 98cb127e971456521ce14ecd8b2f3f8a90b5ce295623c6ad3e8b60495f3b231f
|
|
4
|
+
data.tar.gz: ac3dab036f2d6bfdf07108e82e14c57821010ecbbc0434355b85319f71c63868
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 901e3de9e03327d7750e80b55ef9db13dd615eb258ddef2ec36d0c6063d7b3307689798b626a05fcc6c38e611a0954977f2341de0584f89be103d2e16bfae2e2
|
|
7
|
+
data.tar.gz: b657ab5a20df302fc9e0c5b217937deb50ddec49dc4f87200f95292433e4464eb0c89ae7104b741f787909d2a9b1b5d5f9117e1a2730c3d2c0e3c29cf502e849
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,39 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.6.0] - 2026-10-03
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `Migration#unsubscribed_emails` for `GET /api/migration/v1/unsubscribed_emails`,
|
|
12
|
+
the channel's own suppression list (rows `id`, `email`,
|
|
13
|
+
`broadcast_channel_id`, `created_at`, `updated_at`). `each_record(:unsubscribed_emails)`
|
|
14
|
+
pages it. `suppressions` returns only the global suppression list, so an
|
|
15
|
+
export needs both.
|
|
16
|
+
|
|
17
|
+
## [0.5.0] - 2026-09-25
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- `subscribers.purged` and `subscribers.purge_failed` webhook event types, in
|
|
21
|
+
`Broadcast::Webhook::SUBSCRIBER_EVENTS` and `EVENT_TYPES` (now 34). A purge
|
|
22
|
+
of the whole list sends one of these instead of a `subscriber.deleted` per
|
|
23
|
+
subscriber.
|
|
24
|
+
- `Users` resource (`client.users`) for the admin-only Users API: `list`,
|
|
25
|
+
`get_user`, `create`, `update`, `deactivate`, `activate`, `delete`, plus
|
|
26
|
+
`channel_permissions`/`set_channel_permissions`/`remove_channel_permissions`/
|
|
27
|
+
`bulk_channel_permissions` and `system_permissions`/`update_system_permissions`.
|
|
28
|
+
Requires an admin API token — a channel token raises
|
|
29
|
+
`Broadcast::AuthorizationError`. Sudo users are read-only through this API
|
|
30
|
+
and sudo access can never be granted. `set_channel_permissions` and
|
|
31
|
+
`bulk_channel_permissions` accept exactly one of `permissions:`, `role:`,
|
|
32
|
+
or `preset_id:`, raising `ArgumentError` otherwise; the underlying `PUT` is
|
|
33
|
+
a full replace of the channel's permission record. `Connection` gained PUT
|
|
34
|
+
support to carry this call.
|
|
35
|
+
- `Channel#design` (`client.channel.design`) for `GET /api/v1/channel/design`,
|
|
36
|
+
which returns the token's channel brand kit, fully resolved: colours, font
|
|
37
|
+
key and stack, layout, logo URL, website and social links. Read-only; needs
|
|
38
|
+
the `templates_read` permission.
|
|
39
|
+
|
|
7
40
|
## [0.4.1] - 2026-08-16
|
|
8
41
|
|
|
9
42
|
### Fixed — sequence enrolment never worked
|
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.6.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.6.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
|
@@ -799,6 +799,23 @@ client.templates.update(1, subject: 'Updated subject')
|
|
|
799
799
|
client.templates.delete(1)
|
|
800
800
|
```
|
|
801
801
|
|
|
802
|
+
### Brand Kit
|
|
803
|
+
|
|
804
|
+
Read the channel's brand kit (Settings → Design) so your own templates can
|
|
805
|
+
match it. Read-only, fully resolved (defaults filled in), and always the
|
|
806
|
+
token's own channel, so it takes no arguments. Needs `templates_read`.
|
|
807
|
+
|
|
808
|
+
```ruby
|
|
809
|
+
design = client.channel.design
|
|
810
|
+
design['colors']['accent'] # => '#2563eb'
|
|
811
|
+
design['typography']['font'] # => 'georgia'
|
|
812
|
+
design['typography']['font_stack'] # => email-safe CSS font stack
|
|
813
|
+
design['layout'] # => { 'width' => 600, 'radius' => 8 }
|
|
814
|
+
design['brand']['logo_url'] # => public URL, or nil when no logo is set
|
|
815
|
+
design['brand']['website_url'] # => 'https://acme.example'
|
|
816
|
+
design['brand']['social_links'] # => [{ 'network' => 'x', 'url' => 'https://x.com/acme' }]
|
|
817
|
+
```
|
|
818
|
+
|
|
802
819
|
---
|
|
803
820
|
|
|
804
821
|
## Opt-In Forms
|
|
@@ -962,6 +979,64 @@ admin_client.email_servers.copy_to_channel(99, target_channel_id: 7)
|
|
|
962
979
|
|
|
963
980
|
---
|
|
964
981
|
|
|
982
|
+
## Users
|
|
983
|
+
|
|
984
|
+
Manage installation users and their permissions. **Requires an admin/system API token** -- a regular per-channel token gets `Broadcast::AuthorizationError` ("Admin API token required for user management"). Sudo users are read-only through this API: `update`, `deactivate`, `activate`, `delete`, and any permission write against a sudo user raise `Broadcast::AuthorizationError`. Sudo access can never be granted through this resource.
|
|
985
|
+
|
|
986
|
+
```ruby
|
|
987
|
+
# List / search
|
|
988
|
+
result = admin_client.users.list
|
|
989
|
+
result = admin_client.users.list(q: 'ada', status: 'active', limit: 10, offset: 0)
|
|
990
|
+
|
|
991
|
+
# Get a single user (includes system_permissions and channel_permissions)
|
|
992
|
+
user = admin_client.users.get_user(3)
|
|
993
|
+
|
|
994
|
+
# Create -- one of password: or send_password_reset: true is required
|
|
995
|
+
admin_client.users.create(
|
|
996
|
+
email: 'ada@example.com',
|
|
997
|
+
first_name: 'Ada',
|
|
998
|
+
last_name: 'Lovelace',
|
|
999
|
+
send_password_reset: true
|
|
1000
|
+
)
|
|
1001
|
+
|
|
1002
|
+
# Update -- pass only the fields you want to change
|
|
1003
|
+
admin_client.users.update(3, first_name: 'Grace')
|
|
1004
|
+
|
|
1005
|
+
admin_client.users.deactivate(3)
|
|
1006
|
+
admin_client.users.activate(3) # also clears lockout
|
|
1007
|
+
admin_client.users.delete(3)
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
### Channel Permissions
|
|
1011
|
+
|
|
1012
|
+
`set_channel_permissions` is a `PUT` -- it **replaces the whole channel permission record**; any flag not named under `permissions:` becomes `false`. Pass exactly one of `permissions:`, `role:`, or `preset_id:`; passing zero or more than one raises `ArgumentError` before a request is sent.
|
|
1013
|
+
|
|
1014
|
+
```ruby
|
|
1015
|
+
admin_client.users.channel_permissions(3)
|
|
1016
|
+
|
|
1017
|
+
admin_client.users.set_channel_permissions(3, 7, permissions: { subscribers_read: true, broadcasts_write: true })
|
|
1018
|
+
admin_client.users.set_channel_permissions(3, 7, role: 'Editor')
|
|
1019
|
+
admin_client.users.set_channel_permissions(3, 7, preset_id: 12)
|
|
1020
|
+
|
|
1021
|
+
admin_client.users.remove_channel_permissions(3, 7)
|
|
1022
|
+
|
|
1023
|
+
# Apply the same permissions|role|preset_id to several channels at once.
|
|
1024
|
+
# Returns `applied` and `failed` (per-channel errors) rather than raising.
|
|
1025
|
+
admin_client.users.bulk_channel_permissions(3, broadcast_channel_ids: [7, 8], role: 'Viewer')
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
### System Permissions
|
|
1029
|
+
|
|
1030
|
+
```ruby
|
|
1031
|
+
admin_client.users.system_permissions(3)
|
|
1032
|
+
|
|
1033
|
+
# PATCH changes only the flags named -- others are left as-is. `sudo_access`
|
|
1034
|
+
# is never accepted; sending it is a 422.
|
|
1035
|
+
admin_client.users.update_system_permissions(3, user_management: true)
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
---
|
|
1039
|
+
|
|
965
1040
|
## Suppressions
|
|
966
1041
|
|
|
967
1042
|
A suppressed address is one Broadcast will not email. Each channel has its own list, and the installation has a global one; `client.suppressions` manages the current channel's list:
|
|
@@ -1056,9 +1131,9 @@ secret = result['secret']
|
|
|
1056
1131
|
|
|
1057
1132
|
# Every valid event type is available as a constant. An unknown event type is
|
|
1058
1133
|
# dropped server-side rather than rejected, so subscribe from these.
|
|
1059
|
-
Broadcast::Webhook::EVENT_TYPES # all
|
|
1134
|
+
Broadcast::Webhook::EVENT_TYPES # all 34
|
|
1060
1135
|
Broadcast::Webhook::EMAIL_EVENTS # email.sent, email.delivered, ...
|
|
1061
|
-
Broadcast::Webhook::SUBSCRIBER_EVENTS # subscriber.created,
|
|
1136
|
+
Broadcast::Webhook::SUBSCRIBER_EVENTS # subscriber.created, ..., subscribers.purged
|
|
1062
1137
|
Broadcast::Webhook::BROADCAST_EVENTS # broadcast.sending, broadcast.sent, ...
|
|
1063
1138
|
Broadcast::Webhook::SEQUENCE_EVENTS # sequence.subscriber_added, ...
|
|
1064
1139
|
Broadcast::Webhook::SYSTEM_EVENTS # message.attempt.exhausted, test.webhook
|
|
@@ -1088,11 +1163,14 @@ result['data'] # => [{'id' => 1, 'event_type' => 'email.sent', 'response_status
|
|
|
1088
1163
|
| Category | Events |
|
|
1089
1164
|
|----------|--------|
|
|
1090
1165
|
| Email | `email.sent`, `email.delivered`, `email.delivery_delayed`, `email.opened`, `email.clicked`, `email.bounced`, `email.complained`, `email.failed` |
|
|
1091
|
-
| Subscriber | `subscriber.created`, `subscriber.updated`, `subscriber.deleted`, `subscriber.subscribed`, `subscriber.unsubscribed`, `subscriber.bounced`, `subscriber.complained` |
|
|
1166
|
+
| Subscriber | `subscriber.created`, `subscriber.updated`, `subscriber.deleted`, `subscriber.subscribed`, `subscriber.unsubscribed`, `subscriber.bounced`, `subscriber.complained`, `subscribers.purged`, `subscribers.purge_failed` |
|
|
1092
1167
|
| Broadcast | `broadcast.scheduled`, `broadcast.queueing`, `broadcast.sending`, `broadcast.sent`, `broadcast.failed`, `broadcast.partial_failure`, `broadcast.aborted`, `broadcast.paused` |
|
|
1093
1168
|
| Sequence | `sequence.subscriber_added`, `sequence.subscriber_completed`, `sequence.subscriber_moved`, `sequence.subscriber_removed`, `sequence.subscriber_paused`, `sequence.subscriber_resumed`, `sequence.subscriber_error` |
|
|
1094
1169
|
| System | `message.attempt.exhausted`, `test.webhook` |
|
|
1095
1170
|
|
|
1171
|
+
A purge of a channel's whole list sends one `subscribers.purged` (or
|
|
1172
|
+
`subscribers.purge_failed`) instead of a `subscriber.deleted` per subscriber.
|
|
1173
|
+
|
|
1096
1174
|
### Webhook Signature Verification
|
|
1097
1175
|
|
|
1098
1176
|
All incoming webhooks are signed with HMAC-SHA256. Here's a complete Rails controller:
|
|
@@ -1302,10 +1380,14 @@ Available collections:
|
|
|
1302
1380
|
```
|
|
1303
1381
|
channels subscribers templates segments sequences email_servers
|
|
1304
1382
|
opt_in_forms broadcasts outbound_receipts webhook_endpoints tokens
|
|
1305
|
-
suppressions tags users link_redirects
|
|
1306
|
-
subscriber_histories file_assets
|
|
1383
|
+
suppressions unsubscribed_emails tags users link_redirects
|
|
1384
|
+
link_clicks subscriber_histories file_assets
|
|
1307
1385
|
```
|
|
1308
1386
|
|
|
1387
|
+
`suppressions` is the global suppression list (GlobalSuppression rows, global
|
|
1388
|
+
or scoped to the channel). `unsubscribed_emails` is the channel's own
|
|
1389
|
+
suppression list. Export both to keep every suppressed address.
|
|
1390
|
+
|
|
1309
1391
|
File assets are downloaded separately and return raw bytes:
|
|
1310
1392
|
|
|
1311
1393
|
```ruby
|
|
@@ -1322,8 +1404,8 @@ All API errors inherit from `Broadcast::Error`. Put specific errors before gener
|
|
|
1322
1404
|
```ruby
|
|
1323
1405
|
begin
|
|
1324
1406
|
client.send_email(to: 'user@example.com', subject: 'Hi', body: 'Hello')
|
|
1325
|
-
rescue Broadcast::AuthenticationError # 401 -- invalid or expired API token
|
|
1326
|
-
rescue Broadcast::AuthorizationError # 403 --
|
|
1407
|
+
rescue Broadcast::AuthenticationError # 401 -- invalid or expired API token, or it lacks the required permission
|
|
1408
|
+
rescue Broadcast::AuthorizationError # 403 -- admin-only endpoint, or the user is sudo (read-only)
|
|
1327
1409
|
rescue Broadcast::NotFoundError # 404 -- resource does not exist
|
|
1328
1410
|
rescue Broadcast::ConflictError # 409 -- Idempotency-Key request still in flight
|
|
1329
1411
|
rescue Broadcast::ValidationError # 422 -- missing or invalid parameters
|
|
@@ -1357,9 +1439,10 @@ Each token can be scoped to specific resources. The ActionMailer delivery method
|
|
|
1357
1439
|
| Sequences | `sequences_read` -- list, get, list steps | `sequences_write` -- create, update, delete, manage steps, enroll subscribers |
|
|
1358
1440
|
| Broadcasts | `broadcasts_read` -- list, get, statistics | `broadcasts_write` -- create, update, delete, send, schedule |
|
|
1359
1441
|
| Segments | `segments_read` -- list, get | `segments_write` -- create, update, delete |
|
|
1360
|
-
| Templates | `templates_read` -- list, get | `templates_write` -- create, update, delete |
|
|
1442
|
+
| Templates | `templates_read` -- list, get, channel brand kit (`channel.design`) | `templates_write` -- create, update, delete |
|
|
1361
1443
|
| Opt-In Forms | `opt_in_forms_read` -- list, get, analytics | `opt_in_forms_write` -- create, update, delete, create_variant, duplicate |
|
|
1362
1444
|
| Email Servers | `email_servers_read` -- list, get | `email_servers_write` -- create, update, delete, test_connection, copy_to_channel (admin) |
|
|
1445
|
+
| Users (admin token only) | `users_read` -- list, get, channel_permissions, system_permissions | `users_write` -- create, update, deactivate, activate, delete, permission writes |
|
|
1363
1446
|
| Webhook Endpoints | `webhook_endpoints_read` -- list, get, deliveries | `webhook_endpoints_write` -- create, update, delete, test |
|
|
1364
1447
|
| Autopilot | `autopilot_read` -- list, get, runs | `autopilot_write` -- create, update, delete, activate, pause, deactivate, trigger_run |
|
|
1365
1448
|
| Suppressions | `suppressions_read` -- list, check | `suppressions_write` -- add, remove, bulk add/remove |
|
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` — 34 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
|
@@ -119,6 +119,11 @@ module Broadcast
|
|
|
119
119
|
@email_servers ||= Resources::EmailServers.new(self)
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
+
# Installation users and their permissions. Requires an admin API token.
|
|
123
|
+
def users
|
|
124
|
+
@users ||= Resources::Users.new(self)
|
|
125
|
+
end
|
|
126
|
+
|
|
122
127
|
def autopilots
|
|
123
128
|
@autopilots ||= Resources::Autopilots.new(self)
|
|
124
129
|
end
|
|
@@ -139,6 +144,11 @@ module Broadcast
|
|
|
139
144
|
@global_suppressions ||= Resources::GlobalSuppressions.new(self)
|
|
140
145
|
end
|
|
141
146
|
|
|
147
|
+
# The token's own channel, e.g. `client.channel.design` for its brand kit.
|
|
148
|
+
def channel
|
|
149
|
+
@channel ||= Resources::Channel.new(self)
|
|
150
|
+
end
|
|
151
|
+
|
|
142
152
|
# Read-only export endpoints under /api/migration/v1. Requires an admin
|
|
143
153
|
# (system) API token.
|
|
144
154
|
def migration
|
data/lib/broadcast/connection.rb
CHANGED
|
@@ -118,6 +118,7 @@ module Broadcast
|
|
|
118
118
|
when :get then Net::HTTP::Get
|
|
119
119
|
when :post then Net::HTTP::Post
|
|
120
120
|
when :patch then Net::HTTP::Patch
|
|
121
|
+
when :put then Net::HTTP::Put
|
|
121
122
|
when :delete then Net::HTTP::Delete
|
|
122
123
|
else raise ArgumentError, "Unsupported HTTP method: #{method}"
|
|
123
124
|
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Broadcast
|
|
4
|
+
module Resources
|
|
5
|
+
# The token's own channel. Reads only the channel the token resolves to
|
|
6
|
+
# (or, for an admin token, the channel it is scoped to); there is no
|
|
7
|
+
# channel id argument.
|
|
8
|
+
class Channel < Base
|
|
9
|
+
# The channel's brand kit from Settings -> Design, fully resolved
|
|
10
|
+
# (defaults filled in), read-only. Requires the `templates_read`
|
|
11
|
+
# permission.
|
|
12
|
+
#
|
|
13
|
+
# Returns a Hash with `colors`, `typography` (`font` key and email-safe
|
|
14
|
+
# `font_stack`), `layout` (`width`, `radius`), and `brand` (`logo_url` as
|
|
15
|
+
# a public URL or nil, `logo_width`, `website_url`, `social_links`,
|
|
16
|
+
# `social_icon_style`).
|
|
17
|
+
def design
|
|
18
|
+
get('/api/v1/channel/design')
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -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|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Broadcast
|
|
4
|
+
module Resources
|
|
5
|
+
# Manage installation users, their per-channel permissions, and their
|
|
6
|
+
# system-wide permissions. **Requires an admin API token** — a regular
|
|
7
|
+
# channel token gets `Broadcast::AuthorizationError`
|
|
8
|
+
# ("Admin API token required for user management").
|
|
9
|
+
#
|
|
10
|
+
# Sudo users are read-only through this API: `update`, `deactivate`,
|
|
11
|
+
# `activate`, `delete`, and any permission write against a sudo user
|
|
12
|
+
# raise `Broadcast::AuthorizationError`
|
|
13
|
+
# ("Sudo users cannot be changed through the API"). Sudo access can never
|
|
14
|
+
# be granted through this resource.
|
|
15
|
+
class Users < Base
|
|
16
|
+
# rubocop:disable Naming/MethodParameterName -- `q` matches the API's own query param name
|
|
17
|
+
def list(limit: nil, offset: nil, q: nil, status: nil)
|
|
18
|
+
# rubocop:enable Naming/MethodParameterName
|
|
19
|
+
params = {}
|
|
20
|
+
params[:limit] = limit unless limit.nil?
|
|
21
|
+
params[:offset] = offset unless offset.nil?
|
|
22
|
+
params[:q] = q unless q.nil?
|
|
23
|
+
params[:status] = status unless status.nil?
|
|
24
|
+
get('/api/v1/users', params)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def get_user(id)
|
|
28
|
+
get("/api/v1/users/#{id}")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def create(**attrs)
|
|
32
|
+
post('/api/v1/users', { user: attrs })
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Update a user. Attrs are wrapped under `user:` on the wire. Raises
|
|
36
|
+
# `Broadcast::AuthorizationError` if the target user is a sudo user.
|
|
37
|
+
def update(id, **attrs)
|
|
38
|
+
patch("/api/v1/users/#{id}", { user: attrs })
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def deactivate(id)
|
|
42
|
+
post("/api/v1/users/#{id}/deactivate")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Reactivates the user and also clears any lockout.
|
|
46
|
+
def activate(id)
|
|
47
|
+
post("/api/v1/users/#{id}/activate")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def delete(id)
|
|
51
|
+
@client.request(:delete, "/api/v1/users/#{id}")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def channel_permissions(id)
|
|
55
|
+
get("/api/v1/users/#{id}/channel_permissions")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Set a user's permissions for one channel. This is a PUT: it REPLACES
|
|
59
|
+
# the whole channel permission record, so any flag not named under
|
|
60
|
+
# `permissions:` becomes false. Pass exactly one of `permissions:`,
|
|
61
|
+
# `role:`, or `preset_id:` — anything else raises `ArgumentError`
|
|
62
|
+
# client-side before a request is made. Sudo can never be granted this
|
|
63
|
+
# way, and doing so is rejected server-side.
|
|
64
|
+
def set_channel_permissions(id, broadcast_channel_id, permissions: nil, role: nil, preset_id: nil)
|
|
65
|
+
body = exactly_one_option!(permissions: permissions, role: role, preset_id: preset_id)
|
|
66
|
+
@client.request(:put, "/api/v1/users/#{id}/channel_permissions/#{broadcast_channel_id}", body)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def remove_channel_permissions(id, broadcast_channel_id)
|
|
70
|
+
@client.request(:delete, "/api/v1/users/#{id}/channel_permissions/#{broadcast_channel_id}")
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Apply the same permissions|role|preset_id (exactly one, same rule as
|
|
74
|
+
# `set_channel_permissions`) to several channels at once. Returns
|
|
75
|
+
# `applied` and `failed` (per-channel errors), rather than raising, for
|
|
76
|
+
# channels that could not be updated.
|
|
77
|
+
def bulk_channel_permissions(id, broadcast_channel_ids:, permissions: nil, role: nil, preset_id: nil)
|
|
78
|
+
body = exactly_one_option!(permissions: permissions, role: role, preset_id: preset_id)
|
|
79
|
+
body[:broadcast_channel_ids] = broadcast_channel_ids
|
|
80
|
+
post("/api/v1/users/#{id}/channel_permissions/bulk", body)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def system_permissions(id)
|
|
84
|
+
get("/api/v1/users/#{id}/system_permissions")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Updates only the flags named in `permissions` -- unnamed flags are
|
|
88
|
+
# left as-is. `sudo_access` is never accepted; sending it is a 422.
|
|
89
|
+
def update_system_permissions(id, permissions)
|
|
90
|
+
patch("/api/v1/users/#{id}/system_permissions", { permissions: permissions })
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def exactly_one_option!(permissions:, role:, preset_id:)
|
|
96
|
+
given = { permissions: permissions, role: role, preset_id: preset_id }.compact
|
|
97
|
+
raise ArgumentError, 'Pass exactly one of permissions:, role:, or preset_id:' unless given.size == 1
|
|
98
|
+
|
|
99
|
+
given
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/lib/broadcast/version.rb
CHANGED
data/lib/broadcast/webhook.rb
CHANGED
|
@@ -15,10 +15,12 @@ module Broadcast
|
|
|
15
15
|
email.bounced email.opened email.clicked email.failed
|
|
16
16
|
].freeze
|
|
17
17
|
|
|
18
|
+
# subscribers.* are sent once for a whole-list purge, in place of a
|
|
19
|
+
# subscriber.deleted per row.
|
|
18
20
|
SUBSCRIBER_EVENTS = %w[
|
|
19
21
|
subscriber.created subscriber.updated subscriber.deleted
|
|
20
22
|
subscriber.subscribed subscriber.unsubscribed subscriber.bounced
|
|
21
|
-
subscriber.complained
|
|
23
|
+
subscriber.complained subscribers.purged subscribers.purge_failed
|
|
22
24
|
].freeze
|
|
23
25
|
|
|
24
26
|
BROADCAST_EVENTS = %w[
|
data/lib/broadcast.rb
CHANGED
|
@@ -18,10 +18,12 @@ require_relative 'broadcast/resources/webhook_endpoints'
|
|
|
18
18
|
require_relative 'broadcast/resources/transactionals'
|
|
19
19
|
require_relative 'broadcast/resources/opt_in_forms'
|
|
20
20
|
require_relative 'broadcast/resources/email_servers'
|
|
21
|
+
require_relative 'broadcast/resources/users'
|
|
21
22
|
require_relative 'broadcast/resources/autopilots'
|
|
22
23
|
require_relative 'broadcast/resources/discovery'
|
|
23
24
|
require_relative 'broadcast/resources/suppressions'
|
|
24
25
|
require_relative 'broadcast/resources/global_suppressions'
|
|
26
|
+
require_relative 'broadcast/resources/channel'
|
|
25
27
|
require_relative 'broadcast/resources/migration'
|
|
26
28
|
|
|
27
29
|
# ActionMailer integration — only loaded when Rails is present
|
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.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simon Chiu
|
|
@@ -50,6 +50,7 @@ files:
|
|
|
50
50
|
- lib/broadcast/resources/autopilots.rb
|
|
51
51
|
- lib/broadcast/resources/base.rb
|
|
52
52
|
- lib/broadcast/resources/broadcasts.rb
|
|
53
|
+
- lib/broadcast/resources/channel.rb
|
|
53
54
|
- lib/broadcast/resources/discovery.rb
|
|
54
55
|
- lib/broadcast/resources/email_servers.rb
|
|
55
56
|
- lib/broadcast/resources/global_suppressions.rb
|
|
@@ -61,6 +62,7 @@ files:
|
|
|
61
62
|
- lib/broadcast/resources/suppressions.rb
|
|
62
63
|
- lib/broadcast/resources/templates.rb
|
|
63
64
|
- lib/broadcast/resources/transactionals.rb
|
|
65
|
+
- lib/broadcast/resources/users.rb
|
|
64
66
|
- lib/broadcast/resources/webhook_endpoints.rb
|
|
65
67
|
- lib/broadcast/response.rb
|
|
66
68
|
- lib/broadcast/version.rb
|
|
@@ -88,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
88
90
|
- !ruby/object:Gem::Version
|
|
89
91
|
version: '0'
|
|
90
92
|
requirements: []
|
|
91
|
-
rubygems_version: 4.0.
|
|
93
|
+
rubygems_version: 4.0.10
|
|
92
94
|
specification_version: 4
|
|
93
95
|
summary: Ruby client for the Broadcast email platform
|
|
94
96
|
test_files: []
|