sendly 3.40.0 → 4.0.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 +122 -1
- data/Gemfile.lock +3 -3
- data/README.md +70 -0
- data/lib/sendly/account_resource.rb +4 -4
- data/lib/sendly/campaigns_resource.rb +8 -8
- data/lib/sendly/contacts_resource.rb +9 -9
- data/lib/sendly/enterprise.rb +26 -26
- data/lib/sendly/templates_resource.rb +6 -6
- data/lib/sendly/verify.rb +3 -3
- data/lib/sendly/version.rb +1 -1
- data/lib/sendly/webhooks_resource.rb +10 -10
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 40344d4fa41e8c72544b0b5327b7bc4d717285c38d244397bf0f93ad21ce5b1a
|
|
4
|
+
data.tar.gz: d0bf2bd2c6256ccdbdea1f8c9319c5891b5a030b798a091e7afc59d33752b543
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 20aa959ad9bd7f630c1e26b0a511648aecbfb02d095a789d2d17500e2e035eb02b79fad1c9ae6dc9e0959c41dda68d73c97b1a3d1a795f2dedacd41f8ef03262
|
|
7
|
+
data.tar.gz: 70f56c72f0b628b86e5ea964de3653b7e16d00fa9c4a5214ef14038dbf3da30feedf84b3b489bbfdfd316c42d70f4116ff6b914fe358d7204498d41601a8213a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# sendly (Ruby)
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 4.0.0
|
|
4
|
+
|
|
5
|
+
**Upgrading from 3.40.0:** that release already contained the breaking changes below, published by mistake as a minor version. 4.0.0 carries them under the correct major. Relative to 3.40.0, the only new changes are under **Security**.
|
|
4
6
|
|
|
5
7
|
### Breaking Changes
|
|
6
8
|
|
|
@@ -22,6 +24,120 @@
|
|
|
22
24
|
|
|
23
25
|
- **`id` is no longer filled from an unrelated `id` key.** `contact.auto_flagged` carries the contact under `id` and the message that failed under `message_id`, so `event.data.message_id` returned the *contact* id — a handler that marked that message failed acted on the wrong row. Contact events have no message view at all now; read the message with `event.data[:message_id]`.
|
|
24
26
|
|
|
27
|
+
### Migrating from 3.x
|
|
28
|
+
|
|
29
|
+
Nothing outside webhook handling changed. The client, every resource and every
|
|
30
|
+
`message.*` handler you already have keep working as written; the list below is
|
|
31
|
+
the whole edit.
|
|
32
|
+
|
|
33
|
+
**1. Reading a message field off a lifecycle event now raises.** 3.x decoded
|
|
34
|
+
every `data.object` into a `Sendly::WebhookMessageData`, so an `rcs_agent.live`
|
|
35
|
+
handler that asked for `event.data.from` was handed `""`, `event.data.segments`
|
|
36
|
+
`1` and `event.data.credits_used` `0` — values the event never carried, not
|
|
37
|
+
distinguishable from real ones, and no error was raised. The same calls raise
|
|
38
|
+
`NoMethodError` in 4.0, naming the keys that did arrive. That failure is the
|
|
39
|
+
point of this release rather than an accident of it: the value it replaces was
|
|
40
|
+
wrong, and silently so.
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
# 3.x — silently wrong, on every RCS event
|
|
44
|
+
event.data.from # => ""
|
|
45
|
+
event.data.segments # => 1
|
|
46
|
+
|
|
47
|
+
# 4.0 — the same call
|
|
48
|
+
event.data.from
|
|
49
|
+
# => NoMethodError: undefined method 'from' for Sendly::WebhookObject:
|
|
50
|
+
# this event's data.object carries agent_id, name, stage, organization_id
|
|
51
|
+
|
|
52
|
+
# 4.0 — the edit: read the object the event actually carries
|
|
53
|
+
event.data[:agent_id] # => "bb22cc33-dd44-4e55-9f66-001122334455"
|
|
54
|
+
event.data.stage # => "live"
|
|
55
|
+
event.raw_object # => the whole data.object Hash, untouched
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**2. `event.data` is a message view only on `message.*` events.** In 3.x it was
|
|
59
|
+
a `WebhookMessageData` whatever the event was, so `event.data.id` and
|
|
60
|
+
`event.data.status` answered for anything — with the payload's value when the
|
|
61
|
+
key happened to exist, and with `''` or `nil` when it did not. In 4.0 a
|
|
62
|
+
lifecycle event's `event.data` is a `Sendly::WebhookObject`: the keys the
|
|
63
|
+
payload carried are readable and nothing else is. `event.message` is new in 4.0
|
|
64
|
+
and is the message view; it is `nil` outside `message.*`, so guard it with
|
|
65
|
+
`event.message?` rather than calling into it unconditionally.
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
# 3.x — answered for every event type; "" when the payload had no id
|
|
69
|
+
mark_delivered(event.data.id)
|
|
70
|
+
|
|
71
|
+
# 4.0 — split the branches
|
|
72
|
+
case event.type
|
|
73
|
+
when Sendly::Webhooks::EVENT_MESSAGE_DELIVERED
|
|
74
|
+
mark_delivered(event.message.id) # same value as 3.x
|
|
75
|
+
when Sendly::Webhooks::EVENT_NUMBER_ACTIVATED
|
|
76
|
+
number_active(event.data[:id]) # the number's id, as it always was
|
|
77
|
+
when Sendly::Webhooks::EVENT_RCS_AGENT_LIVE
|
|
78
|
+
agent_went_live(event.data[:agent_id]) # unreachable in 3.x
|
|
79
|
+
end
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**3. `contact.auto_flagged` no longer reports the contact as the message.** The
|
|
83
|
+
payload carries the contact under `id` and the message that failed under
|
|
84
|
+
`message_id`; 3.x filled the message view's `id` from the contact, so a handler
|
|
85
|
+
that marked "the message" failed acted on the wrong record.
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
# 3.x — the CONTACT id, presented as a message id
|
|
89
|
+
event.data.message_id # => "ct_9"
|
|
90
|
+
|
|
91
|
+
# 4.0
|
|
92
|
+
event.data[:id] # => "ct_9" the contact
|
|
93
|
+
event.data[:message_id] # => "msg_77" the message that failed
|
|
94
|
+
event.message # => nil
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**4. Message fields the payload omitted are `nil`, not a default.** On a
|
|
98
|
+
`message.*` event the readers still exist, so this raises nothing — it changes
|
|
99
|
+
what you get. `key?` separates "absent" from "arrived as null".
|
|
100
|
+
|
|
101
|
+
```ruby
|
|
102
|
+
# message.delivered, on a payload that carried no segments
|
|
103
|
+
event.data.segments # 3.x => 1 4.0 => nil
|
|
104
|
+
event.data.credits_used # 3.x => 0 4.0 => nil
|
|
105
|
+
event.data.direction # 3.x => "outbound" 4.0 => nil
|
|
106
|
+
event.data.key?(:segments) # => false when absent, true when it arrived as null
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**5. `event.data.to_h` is now `data.object` as it arrived.** It used to be a
|
|
110
|
+
compacted subset of the typed message fields, dropping `text`, `metadata`,
|
|
111
|
+
`media_urls`, `message_format` and `organization_id` and renaming `message_id`
|
|
112
|
+
to `id`. Code that persisted `to_h` will start seeing the full object.
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
event.data.to_h == event.raw_object # => true, for every event type
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**6. If you want a typed object for a lifecycle event, ask for one.**
|
|
119
|
+
`#object_as` fills a Struct or Data class from the members it declares and
|
|
120
|
+
ignores the rest of the payload, so a field added to the event later cannot
|
|
121
|
+
break the call.
|
|
122
|
+
|
|
123
|
+
```ruby
|
|
124
|
+
AgentLive = Struct.new(:agent_id, :name, :stage)
|
|
125
|
+
agent = event.object_as(AgentLive)
|
|
126
|
+
agent.stage # => "live"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Deprecations
|
|
130
|
+
|
|
131
|
+
- **`Sendly::Webhooks::EVENT_MESSAGE_QUEUED` is deprecated.** The API has never
|
|
132
|
+
emitted `message.queued`, and subscribing to it fails: `client.webhooks.create`
|
|
133
|
+
and `client.webhooks.update` reject any event outside the API's list with a
|
|
134
|
+
400, raised here as `Sendly::ValidationError`. The constant stays exported in
|
|
135
|
+
4.0 so existing code still loads, and will be removed in the next major.
|
|
136
|
+
`message.undelivered` is rejected on subscribe the same way and has never had
|
|
137
|
+
a constant in this SDK. Subscribe to `message.sent`, `message.failed` and
|
|
138
|
+
`message.bounced` instead. Every other `EVENT_*` constant matches the API's
|
|
139
|
+
list exactly.
|
|
140
|
+
|
|
25
141
|
### Minor Changes
|
|
26
142
|
|
|
27
143
|
- **`Sendly::WebhookEvent#raw_object`** carries `data.object` exactly as it arrived, for every event type, and **`#object_as(klass)`** reads it as a type of your choosing (`event.object_as(AgentLive)`). `#object` is an alias for `raw_object`.
|
|
@@ -48,6 +164,11 @@
|
|
|
48
164
|
|
|
49
165
|
- **`Sendly::ValidationError#field_errors` carries the API's `errors` list** (`[{ "path", "message" }, ...]`) when a 400 or 422 response includes one, instead of always being `nil`. RCS registration uses it to say which brand, agent, campaign or device field needs attention.
|
|
50
166
|
|
|
167
|
+
|
|
168
|
+
### Security
|
|
169
|
+
|
|
170
|
+
- **Path parameters are percent-encoded.** Every id you pass is now encoded (`URI.encode_www_form_component`) before it goes into the request path. An id containing `/`, `?` or `#` used to change which endpoint the request reached: an id of `../../account/keys` left its collection and hit another endpoint carrying your API key. Ordinary ids are sent byte-for-byte as before.
|
|
171
|
+
|
|
51
172
|
## 3.38.0
|
|
52
173
|
|
|
53
174
|
### Minor Changes
|
data/Gemfile.lock
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
sendly (
|
|
4
|
+
sendly (4.0.0)
|
|
5
5
|
faraday (~> 2.0)
|
|
6
6
|
faraday-retry (~> 2.0)
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
-
addressable (2.
|
|
11
|
+
addressable (2.8.8)
|
|
12
12
|
public_suffix (>= 2.0.2, < 8.0)
|
|
13
13
|
ast (2.4.3)
|
|
14
14
|
bigdecimal (3.3.1)
|
|
@@ -74,7 +74,7 @@ GEM
|
|
|
74
74
|
unicode-emoji (~> 4.1)
|
|
75
75
|
unicode-emoji (4.1.0)
|
|
76
76
|
uri (1.1.1)
|
|
77
|
-
webmock (3.26.
|
|
77
|
+
webmock (3.26.1)
|
|
78
78
|
addressable (>= 2.8.0)
|
|
79
79
|
crack (>= 0.3.2)
|
|
80
80
|
hashdiff (>= 0.4.0, < 2.0.0)
|
data/README.md
CHANGED
|
@@ -342,6 +342,16 @@ rotation = client.webhooks.rotate_secret("whk_xxx")
|
|
|
342
342
|
client.webhooks.delete("whk_xxx")
|
|
343
343
|
```
|
|
344
344
|
|
|
345
|
+
Subscribe with the `Sendly::Webhooks::EVENT_*` constants rather than string
|
|
346
|
+
literals — a typo then fails at load time instead of in a 400. Every constant
|
|
347
|
+
but one names an event the API accepts on subscribe. The exception is
|
|
348
|
+
`EVENT_MESSAGE_QUEUED`: `message.queued` has never been emitted and is
|
|
349
|
+
rejected on subscribe with a 400 (`Sendly::ValidationError`). It is
|
|
350
|
+
deprecated, kept only so existing code still loads, and will be removed in the
|
|
351
|
+
next major. `message.undelivered` is rejected in the same way and has never had
|
|
352
|
+
a constant here. Subscribe to `message.sent`, `message.failed` and
|
|
353
|
+
`message.bounced` instead.
|
|
354
|
+
|
|
345
355
|
### Receiving events
|
|
346
356
|
|
|
347
357
|
`Sendly::Webhooks.parse_event` verifies the signature and returns a
|
|
@@ -403,6 +413,66 @@ Note that `contact.auto_flagged` carries the contact under `id` and the message
|
|
|
403
413
|
that failed under `message_id`; read the message with
|
|
404
414
|
`event.data[:message_id]`.
|
|
405
415
|
|
|
416
|
+
### Handling a lifecycle event
|
|
417
|
+
|
|
418
|
+
Only `message.*` events carry a message. A lifecycle event — `rcs_*`,
|
|
419
|
+
`whatsapp_*`, `call.*`, `brand.*`, `campaign.*`, `assignment.*`, `number.*`,
|
|
420
|
+
`port*`, `contact*`, `conversation.*`, `draft.*` — carries a different object,
|
|
421
|
+
so `event.message` is `nil` and you read `data.object` off `event.data` or
|
|
422
|
+
`event.raw_object`.
|
|
423
|
+
|
|
424
|
+
```ruby
|
|
425
|
+
require "sendly"
|
|
426
|
+
|
|
427
|
+
# Framework-neutral: hand it the raw request body and a headers Hash.
|
|
428
|
+
def handle_sendly_webhook(raw_body, headers)
|
|
429
|
+
event = Sendly::Webhooks.parse_event(
|
|
430
|
+
raw_body,
|
|
431
|
+
headers["X-Sendly-Signature"],
|
|
432
|
+
ENV.fetch("SENDLY_WEBHOOK_SECRET"),
|
|
433
|
+
timestamp: headers["X-Sendly-Timestamp"]
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
case event.type
|
|
437
|
+
when Sendly::Webhooks::EVENT_RCS_AGENT_LIVE
|
|
438
|
+
# data.object is the agent: agent_id, name, stage
|
|
439
|
+
puts "RCS agent #{event.data[:name]} is #{event.data.stage}"
|
|
440
|
+
puts event.data[:agent_id]
|
|
441
|
+
when Sendly::Webhooks::EVENT_NUMBER_ACTIVATED
|
|
442
|
+
# data.object is the number: id, phone, status, country_code, source
|
|
443
|
+
puts "#{event.data[:phone]} active in #{event.data[:country_code]}"
|
|
444
|
+
when Sendly::Webhooks::EVENT_CONTACT_AUTO_FLAGGED
|
|
445
|
+
# the contact is `id`; the message that failed is `message_id`
|
|
446
|
+
puts "flagged #{event.data[:id]} (#{event.data[:invalid_reason]})"
|
|
447
|
+
puts "from message #{event.data[:message_id]}"
|
|
448
|
+
when Sendly::Webhooks::EVENT_MESSAGE_DELIVERED
|
|
449
|
+
# message.* events, and only these, also get the typed message view
|
|
450
|
+
puts "#{event.message.id} delivered to #{event.message.to}"
|
|
451
|
+
else
|
|
452
|
+
# An event type this SDK predates still parses; raw_object holds all of it.
|
|
453
|
+
puts "unhandled #{event.type}: #{event.raw_object.inspect}"
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
:ok
|
|
457
|
+
rescue Sendly::WebhookSignatureError
|
|
458
|
+
:unauthorized
|
|
459
|
+
end
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Reading a message field off a lifecycle event fails loudly rather than
|
|
463
|
+
answering with a value the event never carried:
|
|
464
|
+
|
|
465
|
+
```ruby
|
|
466
|
+
event.data.from
|
|
467
|
+
# => NoMethodError: undefined method 'from' for Sendly::WebhookObject:
|
|
468
|
+
# this event's data.object carries agent_id, name, stage, organization_id
|
|
469
|
+
event.message # => nil
|
|
470
|
+
event.message.id # => NoMethodError — nil has no #id
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
Branch on `event.type`, or on `event.message?` / `event.verification?`, before
|
|
474
|
+
reaching for a typed view.
|
|
475
|
+
|
|
406
476
|
## Account & Credits
|
|
407
477
|
|
|
408
478
|
```ruby
|
|
@@ -55,7 +55,7 @@ module Sendly
|
|
|
55
55
|
# @param key_id [String] API key ID
|
|
56
56
|
# @return [Sendly::ApiKey]
|
|
57
57
|
def api_key(key_id)
|
|
58
|
-
response = @client.get("/account/keys/#{key_id}")
|
|
58
|
+
response = @client.get("/account/keys/#{URI.encode_www_form_component(key_id)}")
|
|
59
59
|
ApiKey.new(response)
|
|
60
60
|
end
|
|
61
61
|
|
|
@@ -64,7 +64,7 @@ module Sendly
|
|
|
64
64
|
# @param key_id [String] API key ID
|
|
65
65
|
# @return [Hash] Usage statistics
|
|
66
66
|
def api_key_usage(key_id)
|
|
67
|
-
@client.get("/account/keys/#{key_id}/usage")
|
|
67
|
+
@client.get("/account/keys/#{URI.encode_www_form_component(key_id)}/usage")
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
# Create a new API key
|
|
@@ -96,7 +96,7 @@ module Sendly
|
|
|
96
96
|
body = {}
|
|
97
97
|
body[:reason] = reason if reason
|
|
98
98
|
|
|
99
|
-
@client.patch("/account/keys/#{key_id}/revoke", body)
|
|
99
|
+
@client.patch("/account/keys/#{URI.encode_www_form_component(key_id)}/revoke", body)
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
# Rotate an API key.
|
|
@@ -127,7 +127,7 @@ module Sendly
|
|
|
127
127
|
body = {}
|
|
128
128
|
body[:gracePeriodHours] = grace_period_hours unless grace_period_hours.nil?
|
|
129
129
|
|
|
130
|
-
@client.post("/account/keys/#{key_id}/rotate", body)
|
|
130
|
+
@client.post("/account/keys/#{URI.encode_www_form_component(key_id)}/rotate", body)
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
def transfer_credits(target_organization_id:, amount:)
|
|
@@ -131,7 +131,7 @@ module Sendly
|
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
def get(id)
|
|
134
|
-
response = @client.get("/campaigns/#{id}")
|
|
134
|
+
response = @client.get("/campaigns/#{URI.encode_www_form_component(id)}")
|
|
135
135
|
Campaign.new(response)
|
|
136
136
|
end
|
|
137
137
|
|
|
@@ -142,21 +142,21 @@ module Sendly
|
|
|
142
142
|
body[:templateId] = template_id unless template_id.nil?
|
|
143
143
|
body[:contactListIds] = contact_list_ids if contact_list_ids
|
|
144
144
|
|
|
145
|
-
response = @client.patch("/campaigns/#{id}", body)
|
|
145
|
+
response = @client.patch("/campaigns/#{URI.encode_www_form_component(id)}", body)
|
|
146
146
|
Campaign.new(response)
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
def delete(id)
|
|
150
|
-
@client.delete("/campaigns/#{id}")
|
|
150
|
+
@client.delete("/campaigns/#{URI.encode_www_form_component(id)}")
|
|
151
151
|
end
|
|
152
152
|
|
|
153
153
|
def preview(id)
|
|
154
|
-
response = @client.get("/campaigns/#{id}/preview")
|
|
154
|
+
response = @client.get("/campaigns/#{URI.encode_www_form_component(id)}/preview")
|
|
155
155
|
CampaignPreview.new(response)
|
|
156
156
|
end
|
|
157
157
|
|
|
158
158
|
def send_campaign(id)
|
|
159
|
-
response = @client.post("/campaigns/#{id}/send")
|
|
159
|
+
response = @client.post("/campaigns/#{URI.encode_www_form_component(id)}/send")
|
|
160
160
|
Campaign.new(response)
|
|
161
161
|
end
|
|
162
162
|
|
|
@@ -164,17 +164,17 @@ module Sendly
|
|
|
164
164
|
body = { scheduledAt: scheduled_at }
|
|
165
165
|
body[:timezone] = timezone if timezone
|
|
166
166
|
|
|
167
|
-
response = @client.post("/campaigns/#{id}/schedule", body)
|
|
167
|
+
response = @client.post("/campaigns/#{URI.encode_www_form_component(id)}/schedule", body)
|
|
168
168
|
Campaign.new(response)
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
def cancel(id)
|
|
172
|
-
response = @client.post("/campaigns/#{id}/cancel")
|
|
172
|
+
response = @client.post("/campaigns/#{URI.encode_www_form_component(id)}/cancel")
|
|
173
173
|
Campaign.new(response)
|
|
174
174
|
end
|
|
175
175
|
|
|
176
176
|
def clone(id)
|
|
177
|
-
response = @client.post("/campaigns/#{id}/clone")
|
|
177
|
+
response = @client.post("/campaigns/#{URI.encode_www_form_component(id)}/clone")
|
|
178
178
|
Campaign.new(response)
|
|
179
179
|
end
|
|
180
180
|
end
|
|
@@ -114,7 +114,7 @@ module Sendly
|
|
|
114
114
|
params[:limit] = limit if limit
|
|
115
115
|
params[:offset] = offset if offset
|
|
116
116
|
|
|
117
|
-
response = @client.get("/contact-lists/#{id}", params)
|
|
117
|
+
response = @client.get("/contact-lists/#{URI.encode_www_form_component(id)}", params)
|
|
118
118
|
ContactList.new(response)
|
|
119
119
|
end
|
|
120
120
|
|
|
@@ -131,21 +131,21 @@ module Sendly
|
|
|
131
131
|
body[:name] = name if name
|
|
132
132
|
body[:description] = description unless description.nil?
|
|
133
133
|
|
|
134
|
-
response = @client.patch("/contact-lists/#{id}", body)
|
|
134
|
+
response = @client.patch("/contact-lists/#{URI.encode_www_form_component(id)}", body)
|
|
135
135
|
ContactList.new(response)
|
|
136
136
|
end
|
|
137
137
|
|
|
138
138
|
def delete(id)
|
|
139
|
-
@client.delete("/contact-lists/#{id}")
|
|
139
|
+
@client.delete("/contact-lists/#{URI.encode_www_form_component(id)}")
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
def add_contacts(list_id, contact_ids)
|
|
143
|
-
response = @client.post("/contact-lists/#{list_id}/contacts", { contact_ids: contact_ids })
|
|
143
|
+
response = @client.post("/contact-lists/#{URI.encode_www_form_component(list_id)}/contacts", { contact_ids: contact_ids })
|
|
144
144
|
{ added_count: response["added_count"] || response["addedCount"] }
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
def remove_contact(list_id, contact_id)
|
|
148
|
-
@client.delete("/contact-lists/#{list_id}/contacts/#{contact_id}")
|
|
148
|
+
@client.delete("/contact-lists/#{URI.encode_www_form_component(list_id)}/contacts/#{URI.encode_www_form_component(contact_id)}")
|
|
149
149
|
end
|
|
150
150
|
end
|
|
151
151
|
|
|
@@ -175,7 +175,7 @@ module Sendly
|
|
|
175
175
|
end
|
|
176
176
|
|
|
177
177
|
def get(id)
|
|
178
|
-
response = @client.get("/contacts/#{id}")
|
|
178
|
+
response = @client.get("/contacts/#{URI.encode_www_form_component(id)}")
|
|
179
179
|
Contact.new(response)
|
|
180
180
|
end
|
|
181
181
|
|
|
@@ -195,12 +195,12 @@ module Sendly
|
|
|
195
195
|
body[:email] = email unless email.nil?
|
|
196
196
|
body[:metadata] = metadata unless metadata.nil?
|
|
197
197
|
|
|
198
|
-
response = @client.patch("/contacts/#{id}", body)
|
|
198
|
+
response = @client.patch("/contacts/#{URI.encode_www_form_component(id)}", body)
|
|
199
199
|
Contact.new(response)
|
|
200
200
|
end
|
|
201
201
|
|
|
202
202
|
def delete(id)
|
|
203
|
-
@client.delete("/contacts/#{id}")
|
|
203
|
+
@client.delete("/contacts/#{URI.encode_www_form_component(id)}")
|
|
204
204
|
end
|
|
205
205
|
|
|
206
206
|
# Clear the invalid flag on a contact so future campaigns include it again.
|
|
@@ -208,7 +208,7 @@ module Sendly
|
|
|
208
208
|
# error (landline, invalid number) or when a carrier lookup reports they
|
|
209
209
|
# can't receive SMS.
|
|
210
210
|
def mark_valid(id)
|
|
211
|
-
response = @client.post("/contacts/#{id}/mark-valid", {})
|
|
211
|
+
response = @client.post("/contacts/#{URI.encode_www_form_component(id)}/mark-valid", {})
|
|
212
212
|
Contact.new(response)
|
|
213
213
|
end
|
|
214
214
|
|
data/lib/sendly/enterprise.rb
CHANGED
|
@@ -22,13 +22,13 @@ module Sendly
|
|
|
22
22
|
def get(workspace_id)
|
|
23
23
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
24
24
|
|
|
25
|
-
@client.get("/enterprise/workspaces/#{workspace_id}")
|
|
25
|
+
@client.get("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}")
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def delete(workspace_id)
|
|
29
29
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
30
30
|
|
|
31
|
-
@client.delete("/enterprise/workspaces/#{workspace_id}")
|
|
31
|
+
@client.delete("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}")
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# Submit (or resubmit) a verification for an enterprise workspace.
|
|
@@ -89,7 +89,7 @@ module Sendly
|
|
|
89
89
|
body[:privacyUrl] = privacy_url unless privacy_url.nil?
|
|
90
90
|
body[:termsUrl] = terms_url unless terms_url.nil?
|
|
91
91
|
|
|
92
|
-
@client.post("/enterprise/workspaces/#{workspace_id}/verification/submit", body)
|
|
92
|
+
@client.post("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/verification/submit", body)
|
|
93
93
|
end
|
|
94
94
|
|
|
95
95
|
# Convenience alias for resubmits. Identical to +submit_verification+
|
|
@@ -106,7 +106,7 @@ module Sendly
|
|
|
106
106
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
107
107
|
raise ArgumentError, "Source workspace ID is required" if source_workspace_id.nil? || source_workspace_id.empty?
|
|
108
108
|
|
|
109
|
-
@client.post("/enterprise/workspaces/#{workspace_id}/verification/inherit", {
|
|
109
|
+
@client.post("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/verification/inherit", {
|
|
110
110
|
source_workspace_id: source_workspace_id
|
|
111
111
|
})
|
|
112
112
|
end
|
|
@@ -114,7 +114,7 @@ module Sendly
|
|
|
114
114
|
def get_verification(workspace_id)
|
|
115
115
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
116
116
|
|
|
117
|
-
@client.get("/enterprise/workspaces/#{workspace_id}/verification")
|
|
117
|
+
@client.get("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/verification")
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
def transfer_credits(workspace_id, source_workspace_id:, amount:)
|
|
@@ -122,7 +122,7 @@ module Sendly
|
|
|
122
122
|
raise ArgumentError, "Source workspace ID is required" if source_workspace_id.nil? || source_workspace_id.empty?
|
|
123
123
|
raise ArgumentError, "Amount must be a positive number" if !amount.is_a?(Integer) || amount <= 0
|
|
124
124
|
|
|
125
|
-
@client.post("/enterprise/workspaces/#{workspace_id}/transfer-credits", {
|
|
125
|
+
@client.post("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/transfer-credits", {
|
|
126
126
|
source_workspace_id: source_workspace_id,
|
|
127
127
|
amount: amount
|
|
128
128
|
})
|
|
@@ -131,7 +131,7 @@ module Sendly
|
|
|
131
131
|
def get_credits(workspace_id)
|
|
132
132
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
133
133
|
|
|
134
|
-
@client.get("/enterprise/workspaces/#{workspace_id}/credits")
|
|
134
|
+
@client.get("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/credits")
|
|
135
135
|
end
|
|
136
136
|
|
|
137
137
|
def create_key(workspace_id, name: nil, type: nil)
|
|
@@ -141,26 +141,26 @@ module Sendly
|
|
|
141
141
|
body[:name] = name if name
|
|
142
142
|
body[:type] = type if type
|
|
143
143
|
|
|
144
|
-
@client.post("/enterprise/workspaces/#{workspace_id}/keys", body)
|
|
144
|
+
@client.post("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/keys", body)
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
def list_keys(workspace_id)
|
|
148
148
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
149
149
|
|
|
150
|
-
@client.get("/enterprise/workspaces/#{workspace_id}/keys")
|
|
150
|
+
@client.get("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/keys")
|
|
151
151
|
end
|
|
152
152
|
|
|
153
153
|
def revoke_key(workspace_id, key_id)
|
|
154
154
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
155
155
|
raise ArgumentError, "Key ID is required" if key_id.nil? || key_id.empty?
|
|
156
156
|
|
|
157
|
-
@client.delete("/enterprise/workspaces/#{workspace_id}/keys/#{key_id}")
|
|
157
|
+
@client.delete("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/keys/#{URI.encode_www_form_component(key_id)}")
|
|
158
158
|
end
|
|
159
159
|
|
|
160
160
|
def list_opt_in_pages(workspace_id)
|
|
161
161
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
162
162
|
|
|
163
|
-
@client.get("/enterprise/workspaces/#{workspace_id}/opt-in-pages")
|
|
163
|
+
@client.get("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/opt-in-pages")
|
|
164
164
|
end
|
|
165
165
|
|
|
166
166
|
def create_opt_in_page(workspace_id, business_name:, use_case: nil, use_case_summary: nil, sample_messages: nil)
|
|
@@ -172,7 +172,7 @@ module Sendly
|
|
|
172
172
|
body[:useCaseSummary] = use_case_summary if use_case_summary
|
|
173
173
|
body[:sampleMessages] = sample_messages if sample_messages
|
|
174
174
|
|
|
175
|
-
@client.post("/enterprise/workspaces/#{workspace_id}/opt-in-pages", body)
|
|
175
|
+
@client.post("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/opt-in-pages", body)
|
|
176
176
|
end
|
|
177
177
|
|
|
178
178
|
def update_opt_in_page(workspace_id, page_id, logo_url: nil, header_color: nil, button_color: nil, custom_headline: nil, custom_benefits: nil)
|
|
@@ -186,14 +186,14 @@ module Sendly
|
|
|
186
186
|
body[:customHeadline] = custom_headline unless custom_headline.nil?
|
|
187
187
|
body[:customBenefits] = custom_benefits unless custom_benefits.nil?
|
|
188
188
|
|
|
189
|
-
@client.patch("/enterprise/workspaces/#{workspace_id}/opt-in-pages/#{page_id}", body)
|
|
189
|
+
@client.patch("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/opt-in-pages/#{URI.encode_www_form_component(page_id)}", body)
|
|
190
190
|
end
|
|
191
191
|
|
|
192
192
|
def delete_opt_in_page(workspace_id, page_id)
|
|
193
193
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
194
194
|
raise ArgumentError, "Page ID is required" if page_id.nil? || page_id.empty?
|
|
195
195
|
|
|
196
|
-
@client.delete("/enterprise/workspaces/#{workspace_id}/opt-in-pages/#{page_id}")
|
|
196
|
+
@client.delete("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/opt-in-pages/#{URI.encode_www_form_component(page_id)}")
|
|
197
197
|
end
|
|
198
198
|
|
|
199
199
|
def set_webhook(workspace_id, url:, events: nil, description: nil)
|
|
@@ -204,19 +204,19 @@ module Sendly
|
|
|
204
204
|
body[:events] = events if events
|
|
205
205
|
body[:description] = description if description
|
|
206
206
|
|
|
207
|
-
@client.put("/enterprise/workspaces/#{workspace_id}/webhooks", body)
|
|
207
|
+
@client.put("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/webhooks", body)
|
|
208
208
|
end
|
|
209
209
|
|
|
210
210
|
def list_webhooks(workspace_id)
|
|
211
211
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
212
212
|
|
|
213
|
-
@client.get("/enterprise/workspaces/#{workspace_id}/webhooks")
|
|
213
|
+
@client.get("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/webhooks")
|
|
214
214
|
end
|
|
215
215
|
|
|
216
216
|
def delete_webhooks(workspace_id, webhook_id: nil)
|
|
217
217
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
218
218
|
|
|
219
|
-
path = "/enterprise/workspaces/#{workspace_id}/webhooks"
|
|
219
|
+
path = "/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/webhooks"
|
|
220
220
|
path += "?webhookId=#{webhook_id}" if webhook_id
|
|
221
221
|
|
|
222
222
|
@client.delete(path)
|
|
@@ -225,7 +225,7 @@ module Sendly
|
|
|
225
225
|
def test_webhook(workspace_id)
|
|
226
226
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
227
227
|
|
|
228
|
-
@client.post("/enterprise/workspaces/#{workspace_id}/webhooks/test")
|
|
228
|
+
@client.post("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/webhooks/test")
|
|
229
229
|
end
|
|
230
230
|
|
|
231
231
|
def suspend(workspace_id, reason: nil)
|
|
@@ -234,13 +234,13 @@ module Sendly
|
|
|
234
234
|
body = {}
|
|
235
235
|
body[:reason] = reason if reason
|
|
236
236
|
|
|
237
|
-
@client.post("/enterprise/workspaces/#{workspace_id}/suspend", body)
|
|
237
|
+
@client.post("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/suspend", body)
|
|
238
238
|
end
|
|
239
239
|
|
|
240
240
|
def resume(workspace_id)
|
|
241
241
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
242
242
|
|
|
243
|
-
@client.post("/enterprise/workspaces/#{workspace_id}/resume")
|
|
243
|
+
@client.post("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/resume")
|
|
244
244
|
end
|
|
245
245
|
|
|
246
246
|
def provision_bulk(workspaces)
|
|
@@ -255,7 +255,7 @@ module Sendly
|
|
|
255
255
|
raise ArgumentError, "Page ID is required" if page_id.nil? || page_id.empty?
|
|
256
256
|
raise ArgumentError, "Domain is required" if domain.nil? || domain.empty?
|
|
257
257
|
|
|
258
|
-
@client.put("/enterprise/workspaces/#{workspace_id}/pages/#{page_id}/domain", { domain: domain })
|
|
258
|
+
@client.put("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/pages/#{URI.encode_www_form_component(page_id)}/domain", { domain: domain })
|
|
259
259
|
end
|
|
260
260
|
|
|
261
261
|
def send_invitation(workspace_id, email:, role:)
|
|
@@ -263,7 +263,7 @@ module Sendly
|
|
|
263
263
|
raise ArgumentError, "Email is required" if email.nil? || email.empty?
|
|
264
264
|
raise ArgumentError, "Role is required" if role.nil? || role.empty?
|
|
265
265
|
|
|
266
|
-
@client.post("/enterprise/workspaces/#{workspace_id}/invitations", {
|
|
266
|
+
@client.post("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/invitations", {
|
|
267
267
|
email: email,
|
|
268
268
|
role: role
|
|
269
269
|
})
|
|
@@ -272,26 +272,26 @@ module Sendly
|
|
|
272
272
|
def list_invitations(workspace_id)
|
|
273
273
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
274
274
|
|
|
275
|
-
@client.get("/enterprise/workspaces/#{workspace_id}/invitations")
|
|
275
|
+
@client.get("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/invitations")
|
|
276
276
|
end
|
|
277
277
|
|
|
278
278
|
def cancel_invitation(workspace_id, invite_id)
|
|
279
279
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
280
280
|
raise ArgumentError, "Invite ID is required" if invite_id.nil? || invite_id.empty?
|
|
281
281
|
|
|
282
|
-
@client.delete("/enterprise/workspaces/#{workspace_id}/invitations/#{invite_id}")
|
|
282
|
+
@client.delete("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/invitations/#{URI.encode_www_form_component(invite_id)}")
|
|
283
283
|
end
|
|
284
284
|
|
|
285
285
|
def get_quota(workspace_id)
|
|
286
286
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
287
287
|
|
|
288
|
-
@client.get("/enterprise/workspaces/#{workspace_id}/quota")
|
|
288
|
+
@client.get("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/quota")
|
|
289
289
|
end
|
|
290
290
|
|
|
291
291
|
def set_quota(workspace_id, monthly_message_quota:)
|
|
292
292
|
raise ArgumentError, "Workspace ID is required" if workspace_id.nil? || workspace_id.empty?
|
|
293
293
|
|
|
294
|
-
@client.put("/enterprise/workspaces/#{workspace_id}/quota", {
|
|
294
|
+
@client.put("/enterprise/workspaces/#{URI.encode_www_form_component(workspace_id)}/quota", {
|
|
295
295
|
monthlyMessageQuota: monthly_message_quota
|
|
296
296
|
})
|
|
297
297
|
end
|
|
@@ -129,7 +129,7 @@ module Sendly
|
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
def get(id)
|
|
132
|
-
response = @client.get("/templates/#{id}")
|
|
132
|
+
response = @client.get("/templates/#{URI.encode_www_form_component(id)}")
|
|
133
133
|
Template.new(response)
|
|
134
134
|
end
|
|
135
135
|
|
|
@@ -198,16 +198,16 @@ module Sendly
|
|
|
198
198
|
request_body[:name] = name if name
|
|
199
199
|
request_body[:text] = content if content
|
|
200
200
|
|
|
201
|
-
response = @client.patch("/templates/#{id}", request_body)
|
|
201
|
+
response = @client.patch("/templates/#{URI.encode_www_form_component(id)}", request_body)
|
|
202
202
|
Template.new(response)
|
|
203
203
|
end
|
|
204
204
|
|
|
205
205
|
def delete(id)
|
|
206
|
-
@client.delete("/templates/#{id}")
|
|
206
|
+
@client.delete("/templates/#{URI.encode_www_form_component(id)}")
|
|
207
207
|
end
|
|
208
208
|
|
|
209
209
|
def publish(id)
|
|
210
|
-
response = @client.post("/templates/#{id}/publish")
|
|
210
|
+
response = @client.post("/templates/#{URI.encode_www_form_component(id)}/publish")
|
|
211
211
|
Template.new(response)
|
|
212
212
|
end
|
|
213
213
|
|
|
@@ -217,7 +217,7 @@ module Sendly
|
|
|
217
217
|
# this call fails with a 404. To retire a published template today,
|
|
218
218
|
# {#create} and {#publish} a replacement, then {#delete} this one.
|
|
219
219
|
def unpublish(id)
|
|
220
|
-
response = @client.post("/templates/#{id}/unpublish")
|
|
220
|
+
response = @client.post("/templates/#{URI.encode_www_form_component(id)}/unpublish")
|
|
221
221
|
Template.new(response)
|
|
222
222
|
end
|
|
223
223
|
|
|
@@ -229,7 +229,7 @@ module Sendly
|
|
|
229
229
|
def clone(id, name: nil)
|
|
230
230
|
body = {}
|
|
231
231
|
body[:name] = name if name
|
|
232
|
-
response = @client.post("/templates/#{id}/clone", body)
|
|
232
|
+
response = @client.post("/templates/#{URI.encode_www_form_component(id)}/clone", body)
|
|
233
233
|
Template.new(response)
|
|
234
234
|
end
|
|
235
235
|
|
data/lib/sendly/verify.rb
CHANGED
|
@@ -181,17 +181,17 @@ module Sendly
|
|
|
181
181
|
end
|
|
182
182
|
|
|
183
183
|
def resend(id)
|
|
184
|
-
response = @client.post("/verify/#{id}/resend")
|
|
184
|
+
response = @client.post("/verify/#{URI.encode_www_form_component(id)}/resend")
|
|
185
185
|
SendVerificationResponse.new(response)
|
|
186
186
|
end
|
|
187
187
|
|
|
188
188
|
def check(id, code:)
|
|
189
|
-
response = @client.post("/verify/#{id}/check", { code: code })
|
|
189
|
+
response = @client.post("/verify/#{URI.encode_www_form_component(id)}/check", { code: code })
|
|
190
190
|
CheckVerificationResponse.new(response)
|
|
191
191
|
end
|
|
192
192
|
|
|
193
193
|
def get(id)
|
|
194
|
-
response = @client.get("/verify/#{id}")
|
|
194
|
+
response = @client.get("/verify/#{URI.encode_www_form_component(id)}")
|
|
195
195
|
Verification.new(response)
|
|
196
196
|
end
|
|
197
197
|
|
data/lib/sendly/version.rb
CHANGED
|
@@ -51,7 +51,7 @@ module Sendly
|
|
|
51
51
|
# @return [Sendly::Webhook]
|
|
52
52
|
def get(webhook_id)
|
|
53
53
|
validate_webhook_id!(webhook_id)
|
|
54
|
-
response = @client.get("/webhooks/#{webhook_id}")
|
|
54
|
+
response = @client.get("/webhooks/#{URI.encode_www_form_component(webhook_id)}")
|
|
55
55
|
Webhook.new(response)
|
|
56
56
|
end
|
|
57
57
|
|
|
@@ -77,7 +77,7 @@ module Sendly
|
|
|
77
77
|
body[:mode] = mode unless mode.nil?
|
|
78
78
|
body[:metadata] = metadata unless metadata.nil?
|
|
79
79
|
|
|
80
|
-
response = @client.patch("/webhooks/#{webhook_id}", body)
|
|
80
|
+
response = @client.patch("/webhooks/#{URI.encode_www_form_component(webhook_id)}", body)
|
|
81
81
|
Webhook.new(response)
|
|
82
82
|
end
|
|
83
83
|
|
|
@@ -87,7 +87,7 @@ module Sendly
|
|
|
87
87
|
# @return [void]
|
|
88
88
|
def delete(webhook_id)
|
|
89
89
|
validate_webhook_id!(webhook_id)
|
|
90
|
-
@client.delete("/webhooks/#{webhook_id}")
|
|
90
|
+
@client.delete("/webhooks/#{URI.encode_www_form_component(webhook_id)}")
|
|
91
91
|
nil
|
|
92
92
|
end
|
|
93
93
|
|
|
@@ -97,7 +97,7 @@ module Sendly
|
|
|
97
97
|
# @return [Sendly::WebhookTestResult]
|
|
98
98
|
def test(webhook_id)
|
|
99
99
|
validate_webhook_id!(webhook_id)
|
|
100
|
-
response = @client.post("/webhooks/#{webhook_id}/test")
|
|
100
|
+
response = @client.post("/webhooks/#{URI.encode_www_form_component(webhook_id)}/test")
|
|
101
101
|
WebhookTestResult.new(response)
|
|
102
102
|
end
|
|
103
103
|
|
|
@@ -107,7 +107,7 @@ module Sendly
|
|
|
107
107
|
# @return [Hash] Reset confirmation with updated webhook
|
|
108
108
|
def reset_circuit(webhook_id)
|
|
109
109
|
validate_webhook_id!(webhook_id)
|
|
110
|
-
@client.post("/webhooks/#{webhook_id}/reset-circuit")
|
|
110
|
+
@client.post("/webhooks/#{URI.encode_www_form_component(webhook_id)}/reset-circuit")
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
# Replay failed or cancelled webhook deliveries from the audit log.
|
|
@@ -133,7 +133,7 @@ module Sendly
|
|
|
133
133
|
body[:event_types] = event_types unless event_types.nil?
|
|
134
134
|
body[:statuses] = statuses unless statuses.nil?
|
|
135
135
|
body[:limit] = limit unless limit.nil?
|
|
136
|
-
@client.post("/webhooks/#{webhook_id}/redeliver", body)
|
|
136
|
+
@client.post("/webhooks/#{URI.encode_www_form_component(webhook_id)}/redeliver", body)
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
# Backfill missed webhook events from the underlying message log.
|
|
@@ -157,7 +157,7 @@ module Sendly
|
|
|
157
157
|
body[:until] = until_ unless until_.nil?
|
|
158
158
|
body[:event_types] = event_types unless event_types.nil?
|
|
159
159
|
body[:limit] = limit unless limit.nil?
|
|
160
|
-
@client.post("/webhooks/#{webhook_id}/backfill", body)
|
|
160
|
+
@client.post("/webhooks/#{URI.encode_www_form_component(webhook_id)}/backfill", body)
|
|
161
161
|
end
|
|
162
162
|
|
|
163
163
|
# Rotate the webhook signing secret
|
|
@@ -166,7 +166,7 @@ module Sendly
|
|
|
166
166
|
# @return [Sendly::WebhookSecretRotation]
|
|
167
167
|
def rotate_secret(webhook_id)
|
|
168
168
|
validate_webhook_id!(webhook_id)
|
|
169
|
-
response = @client.post("/webhooks/#{webhook_id}/rotate-secret")
|
|
169
|
+
response = @client.post("/webhooks/#{URI.encode_www_form_component(webhook_id)}/rotate-secret")
|
|
170
170
|
WebhookSecretRotation.new(response)
|
|
171
171
|
end
|
|
172
172
|
|
|
@@ -176,7 +176,7 @@ module Sendly
|
|
|
176
176
|
# @return [Array<Sendly::WebhookDelivery>]
|
|
177
177
|
def deliveries(webhook_id)
|
|
178
178
|
validate_webhook_id!(webhook_id)
|
|
179
|
-
response = @client.get("/webhooks/#{webhook_id}/deliveries")
|
|
179
|
+
response = @client.get("/webhooks/#{URI.encode_www_form_component(webhook_id)}/deliveries")
|
|
180
180
|
response.map { |data| WebhookDelivery.new(data) }
|
|
181
181
|
end
|
|
182
182
|
|
|
@@ -188,7 +188,7 @@ module Sendly
|
|
|
188
188
|
def retry_delivery(webhook_id, delivery_id)
|
|
189
189
|
validate_webhook_id!(webhook_id)
|
|
190
190
|
validate_delivery_id!(delivery_id)
|
|
191
|
-
@client.post("/webhooks/#{webhook_id}/deliveries/#{delivery_id}/retry")
|
|
191
|
+
@client.post("/webhooks/#{URI.encode_www_form_component(webhook_id)}/deliveries/#{URI.encode_www_form_component(delivery_id)}/retry")
|
|
192
192
|
nil
|
|
193
193
|
end
|
|
194
194
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sendly
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 4.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sendly
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|