sendly 1.1.0 → 1.5.1
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/Gemfile.lock +1 -1
- data/README.md +13 -6
- data/lib/sendly/client.rb +0 -25
- data/lib/sendly/types.rb +2 -295
- data/lib/sendly/version.rb +1 -1
- data/lib/sendly.rb +0 -2
- metadata +3 -8
- data/lib/sendly/account_resource.rb +0 -70
- data/lib/sendly/webhooks_resource.rb +0 -149
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8287e0f6c55aa228285671746446a7eb04577569e56515ed76571da61a0392b0
|
|
4
|
+
data.tar.gz: 62017808d07348e8d6e5809b367300e11976a754e7c3323c8155d2d9140befb4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2c3a234e3caa740975c00ad3c1454dbe24f423385f480f180e33f1ac0ef890bbe019f58606ff3f50bb0b508280d9352997dd20b88a6705d277d493235d3ecc57
|
|
7
|
+
data.tar.gz: 5e7cba5b80ec78994f3de8b0dfe5c3f63f6c00cede6f5a98c3d7a679755386ee543477f588c672c248181dbfc279bd8834c5108f1a1071378f9e51b23ea94c40
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -4,17 +4,24 @@ Official Ruby SDK for the Sendly SMS API.
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
# gem
|
|
9
|
-
gem install sendly
|
|
7
|
+
Add to your Gemfile:
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
```ruby
|
|
12
10
|
gem 'sendly'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then run:
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
```bash
|
|
15
16
|
bundle install
|
|
16
17
|
```
|
|
17
18
|
|
|
19
|
+
Or install directly:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gem install sendly
|
|
23
|
+
```
|
|
24
|
+
|
|
18
25
|
## Quick Start
|
|
19
26
|
|
|
20
27
|
```ruby
|
|
@@ -75,7 +82,7 @@ Sendly.send_message(to: "+15551234567", text: "Hello!")
|
|
|
75
82
|
```ruby
|
|
76
83
|
client = Sendly::Client.new(
|
|
77
84
|
"sk_live_v1_xxx",
|
|
78
|
-
base_url: "https://sendly.live/
|
|
85
|
+
base_url: "https://api.sendly.live/v1",
|
|
79
86
|
timeout: 60,
|
|
80
87
|
max_retries: 5
|
|
81
88
|
)
|
data/lib/sendly/client.rb
CHANGED
|
@@ -45,20 +45,6 @@ module Sendly
|
|
|
45
45
|
@messages ||= Messages.new(self)
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
-
# Access the Webhooks resource
|
|
49
|
-
#
|
|
50
|
-
# @return [Sendly::WebhooksResource]
|
|
51
|
-
def webhooks
|
|
52
|
-
@webhooks ||= WebhooksResource.new(self)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Access the Account resource
|
|
56
|
-
#
|
|
57
|
-
# @return [Sendly::AccountResource]
|
|
58
|
-
def account
|
|
59
|
-
@account ||= AccountResource.new(self)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
48
|
# Make a GET request
|
|
63
49
|
#
|
|
64
50
|
# @param path [String] API path
|
|
@@ -77,15 +63,6 @@ module Sendly
|
|
|
77
63
|
request(:post, path, body: body)
|
|
78
64
|
end
|
|
79
65
|
|
|
80
|
-
# Make a PATCH request
|
|
81
|
-
#
|
|
82
|
-
# @param path [String] API path
|
|
83
|
-
# @param body [Hash] Request body
|
|
84
|
-
# @return [Hash] Response body
|
|
85
|
-
def patch(path, body = {})
|
|
86
|
-
request(:patch, path, body: body)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
66
|
# Make a DELETE request
|
|
90
67
|
#
|
|
91
68
|
# @param path [String] API path
|
|
@@ -160,8 +137,6 @@ module Sendly
|
|
|
160
137
|
Net::HTTP::Get.new(uri)
|
|
161
138
|
when :post
|
|
162
139
|
Net::HTTP::Post.new(uri)
|
|
163
|
-
when :patch
|
|
164
|
-
Net::HTTP::Patch.new(uri)
|
|
165
140
|
when :delete
|
|
166
141
|
Net::HTTP::Delete.new(uri)
|
|
167
142
|
else
|
data/lib/sendly/types.rb
CHANGED
|
@@ -18,9 +18,6 @@ module Sendly
|
|
|
18
18
|
# @return [String] Delivery status
|
|
19
19
|
attr_reader :status
|
|
20
20
|
|
|
21
|
-
# @return [String] Message direction (outbound or inbound)
|
|
22
|
-
attr_reader :direction
|
|
23
|
-
|
|
24
21
|
# @return [String, nil] Error message if failed
|
|
25
22
|
attr_reader :error
|
|
26
23
|
|
|
@@ -33,29 +30,14 @@ module Sendly
|
|
|
33
30
|
# @return [Boolean] Whether sent in sandbox mode
|
|
34
31
|
attr_reader :is_sandbox
|
|
35
32
|
|
|
36
|
-
# @return [String, nil] How the message was sent (number_pool, alphanumeric, sandbox)
|
|
37
|
-
attr_reader :sender_type
|
|
38
|
-
|
|
39
|
-
# @return [String, nil] Telnyx message ID for tracking
|
|
40
|
-
attr_reader :telnyx_message_id
|
|
41
|
-
|
|
42
|
-
# @return [String, nil] Warning message
|
|
43
|
-
attr_reader :warning
|
|
44
|
-
|
|
45
|
-
# @return [String, nil] Note about sender behavior
|
|
46
|
-
attr_reader :sender_note
|
|
47
|
-
|
|
48
33
|
# @return [Time, nil] Creation timestamp
|
|
49
34
|
attr_reader :created_at
|
|
50
35
|
|
|
51
36
|
# @return [Time, nil] Delivery timestamp
|
|
52
37
|
attr_reader :delivered_at
|
|
53
38
|
|
|
54
|
-
# Message status constants
|
|
55
|
-
STATUSES = %w[queued sent delivered failed].freeze
|
|
56
|
-
|
|
57
|
-
# Sender type constants
|
|
58
|
-
SENDER_TYPES = %w[number_pool alphanumeric sandbox].freeze
|
|
39
|
+
# Message status constants
|
|
40
|
+
STATUSES = %w[queued sending sent delivered failed].freeze
|
|
59
41
|
|
|
60
42
|
def initialize(data)
|
|
61
43
|
@id = data["id"]
|
|
@@ -63,15 +45,10 @@ module Sendly
|
|
|
63
45
|
@from = data["from"]
|
|
64
46
|
@text = data["text"]
|
|
65
47
|
@status = data["status"]
|
|
66
|
-
@direction = data["direction"] || "outbound"
|
|
67
48
|
@error = data["error"]
|
|
68
49
|
@segments = data["segments"] || 1
|
|
69
50
|
@credits_used = data["creditsUsed"] || 0
|
|
70
51
|
@is_sandbox = data["isSandbox"] || false
|
|
71
|
-
@sender_type = data["senderType"]
|
|
72
|
-
@telnyx_message_id = data["telnyxMessageId"]
|
|
73
|
-
@warning = data["warning"]
|
|
74
|
-
@sender_note = data["senderNote"]
|
|
75
52
|
@created_at = parse_time(data["createdAt"])
|
|
76
53
|
@delivered_at = parse_time(data["deliveredAt"])
|
|
77
54
|
end
|
|
@@ -103,15 +80,10 @@ module Sendly
|
|
|
103
80
|
from: from,
|
|
104
81
|
text: text,
|
|
105
82
|
status: status,
|
|
106
|
-
direction: direction,
|
|
107
83
|
error: error,
|
|
108
84
|
segments: segments,
|
|
109
85
|
credits_used: credits_used,
|
|
110
86
|
is_sandbox: is_sandbox,
|
|
111
|
-
sender_type: sender_type,
|
|
112
|
-
telnyx_message_id: telnyx_message_id,
|
|
113
|
-
warning: warning,
|
|
114
|
-
sender_note: sender_note,
|
|
115
87
|
created_at: created_at&.iso8601,
|
|
116
88
|
delivered_at: delivered_at&.iso8601
|
|
117
89
|
}.compact
|
|
@@ -187,269 +159,4 @@ module Sendly
|
|
|
187
159
|
data.last
|
|
188
160
|
end
|
|
189
161
|
end
|
|
190
|
-
|
|
191
|
-
# ============================================================================
|
|
192
|
-
# Webhooks
|
|
193
|
-
# ============================================================================
|
|
194
|
-
|
|
195
|
-
# Represents a configured webhook endpoint
|
|
196
|
-
class Webhook
|
|
197
|
-
attr_reader :id, :url, :events, :description, :is_active, :failure_count,
|
|
198
|
-
:last_failure_at, :circuit_state, :circuit_opened_at, :api_version,
|
|
199
|
-
:metadata, :created_at, :updated_at, :total_deliveries,
|
|
200
|
-
:successful_deliveries, :success_rate, :last_delivery_at
|
|
201
|
-
|
|
202
|
-
# Circuit state constants
|
|
203
|
-
CIRCUIT_STATES = %w[closed open half_open].freeze
|
|
204
|
-
|
|
205
|
-
def initialize(data)
|
|
206
|
-
@id = data["id"]
|
|
207
|
-
@url = data["url"]
|
|
208
|
-
@events = data["events"] || []
|
|
209
|
-
@description = data["description"]
|
|
210
|
-
# Handle both snake_case API response and camelCase
|
|
211
|
-
@is_active = data["is_active"] || data["isActive"] || false
|
|
212
|
-
@failure_count = data["failure_count"] || data["failureCount"] || 0
|
|
213
|
-
@last_failure_at = parse_time(data["last_failure_at"] || data["lastFailureAt"])
|
|
214
|
-
@circuit_state = data["circuit_state"] || data["circuitState"] || "closed"
|
|
215
|
-
@circuit_opened_at = parse_time(data["circuit_opened_at"] || data["circuitOpenedAt"])
|
|
216
|
-
@api_version = data["api_version"] || data["apiVersion"] || "2024-01"
|
|
217
|
-
@metadata = data["metadata"] || {}
|
|
218
|
-
@created_at = parse_time(data["created_at"] || data["createdAt"])
|
|
219
|
-
@updated_at = parse_time(data["updated_at"] || data["updatedAt"])
|
|
220
|
-
@total_deliveries = data["total_deliveries"] || data["totalDeliveries"] || 0
|
|
221
|
-
@successful_deliveries = data["successful_deliveries"] || data["successfulDeliveries"] || 0
|
|
222
|
-
@success_rate = data["success_rate"] || data["successRate"] || 0
|
|
223
|
-
@last_delivery_at = parse_time(data["last_delivery_at"] || data["lastDeliveryAt"])
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
def active?
|
|
227
|
-
is_active
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def circuit_open?
|
|
231
|
-
circuit_state == "open"
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
def to_h
|
|
235
|
-
{
|
|
236
|
-
id: id, url: url, events: events, description: description,
|
|
237
|
-
is_active: is_active, failure_count: failure_count,
|
|
238
|
-
circuit_state: circuit_state, api_version: api_version,
|
|
239
|
-
metadata: metadata, total_deliveries: total_deliveries,
|
|
240
|
-
successful_deliveries: successful_deliveries, success_rate: success_rate
|
|
241
|
-
}.compact
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
private
|
|
245
|
-
|
|
246
|
-
def parse_time(value)
|
|
247
|
-
return nil if value.nil?
|
|
248
|
-
Time.parse(value)
|
|
249
|
-
rescue ArgumentError
|
|
250
|
-
nil
|
|
251
|
-
end
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
# Webhook with secret (returned on creation)
|
|
255
|
-
class WebhookCreatedResponse < Webhook
|
|
256
|
-
attr_reader :secret
|
|
257
|
-
|
|
258
|
-
def initialize(data)
|
|
259
|
-
super(data)
|
|
260
|
-
@secret = data["secret"]
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
# Represents a webhook delivery attempt
|
|
265
|
-
class WebhookDelivery
|
|
266
|
-
attr_reader :id, :webhook_id, :event_id, :event_type, :attempt_number,
|
|
267
|
-
:max_attempts, :status, :response_status_code, :response_time_ms,
|
|
268
|
-
:error_message, :error_code, :next_retry_at, :created_at, :delivered_at
|
|
269
|
-
|
|
270
|
-
# Delivery status constants
|
|
271
|
-
STATUSES = %w[pending delivered failed cancelled].freeze
|
|
272
|
-
|
|
273
|
-
def initialize(data)
|
|
274
|
-
@id = data["id"]
|
|
275
|
-
@webhook_id = data["webhook_id"] || data["webhookId"]
|
|
276
|
-
@event_id = data["event_id"] || data["eventId"]
|
|
277
|
-
@event_type = data["event_type"] || data["eventType"]
|
|
278
|
-
@attempt_number = data["attempt_number"] || data["attemptNumber"] || 1
|
|
279
|
-
@max_attempts = data["max_attempts"] || data["maxAttempts"] || 6
|
|
280
|
-
@status = data["status"]
|
|
281
|
-
@response_status_code = data["response_status_code"] || data["responseStatusCode"]
|
|
282
|
-
@response_time_ms = data["response_time_ms"] || data["responseTimeMs"]
|
|
283
|
-
@error_message = data["error_message"] || data["errorMessage"]
|
|
284
|
-
@error_code = data["error_code"] || data["errorCode"]
|
|
285
|
-
@next_retry_at = parse_time(data["next_retry_at"] || data["nextRetryAt"])
|
|
286
|
-
@created_at = parse_time(data["created_at"] || data["createdAt"])
|
|
287
|
-
@delivered_at = parse_time(data["delivered_at"] || data["deliveredAt"])
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
def delivered?
|
|
291
|
-
status == "delivered"
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
def failed?
|
|
295
|
-
status == "failed"
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
private
|
|
299
|
-
|
|
300
|
-
def parse_time(value)
|
|
301
|
-
return nil if value.nil?
|
|
302
|
-
Time.parse(value)
|
|
303
|
-
rescue ArgumentError
|
|
304
|
-
nil
|
|
305
|
-
end
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
# Result of testing a webhook
|
|
309
|
-
class WebhookTestResult
|
|
310
|
-
attr_reader :success, :status_code, :response_time_ms, :error
|
|
311
|
-
|
|
312
|
-
def initialize(data)
|
|
313
|
-
@success = data["success"]
|
|
314
|
-
@status_code = data["status_code"] || data["statusCode"]
|
|
315
|
-
@response_time_ms = data["response_time_ms"] || data["responseTimeMs"]
|
|
316
|
-
@error = data["error"]
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
def success?
|
|
320
|
-
success
|
|
321
|
-
end
|
|
322
|
-
end
|
|
323
|
-
|
|
324
|
-
# Result of rotating webhook secret
|
|
325
|
-
class WebhookSecretRotation
|
|
326
|
-
attr_reader :webhook, :new_secret, :old_secret_expires_at, :message
|
|
327
|
-
|
|
328
|
-
def initialize(data)
|
|
329
|
-
@webhook = Webhook.new(data["webhook"])
|
|
330
|
-
@new_secret = data["new_secret"] || data["newSecret"]
|
|
331
|
-
@old_secret_expires_at = parse_time(data["old_secret_expires_at"] || data["oldSecretExpiresAt"])
|
|
332
|
-
@message = data["message"]
|
|
333
|
-
end
|
|
334
|
-
|
|
335
|
-
private
|
|
336
|
-
|
|
337
|
-
def parse_time(value)
|
|
338
|
-
return nil if value.nil?
|
|
339
|
-
Time.parse(value)
|
|
340
|
-
rescue ArgumentError
|
|
341
|
-
nil
|
|
342
|
-
end
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
# ============================================================================
|
|
346
|
-
# Account & Credits
|
|
347
|
-
# ============================================================================
|
|
348
|
-
|
|
349
|
-
# Represents account information
|
|
350
|
-
class Account
|
|
351
|
-
attr_reader :id, :email, :name, :created_at
|
|
352
|
-
|
|
353
|
-
def initialize(data)
|
|
354
|
-
@id = data["id"]
|
|
355
|
-
@email = data["email"]
|
|
356
|
-
@name = data["name"]
|
|
357
|
-
@created_at = parse_time(data["created_at"] || data["createdAt"])
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
private
|
|
361
|
-
|
|
362
|
-
def parse_time(value)
|
|
363
|
-
return nil if value.nil?
|
|
364
|
-
Time.parse(value)
|
|
365
|
-
rescue ArgumentError
|
|
366
|
-
nil
|
|
367
|
-
end
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
# Represents credit balance information
|
|
371
|
-
class Credits
|
|
372
|
-
attr_reader :balance, :reserved_balance, :available_balance
|
|
373
|
-
|
|
374
|
-
def initialize(data)
|
|
375
|
-
@balance = data["balance"] || 0
|
|
376
|
-
@reserved_balance = data["reserved_balance"] || data["reservedBalance"] || 0
|
|
377
|
-
@available_balance = data["available_balance"] || data["availableBalance"] || 0
|
|
378
|
-
end
|
|
379
|
-
end
|
|
380
|
-
|
|
381
|
-
# Represents a credit transaction
|
|
382
|
-
class CreditTransaction
|
|
383
|
-
attr_reader :id, :type, :amount, :balance_after, :description, :message_id, :created_at
|
|
384
|
-
|
|
385
|
-
# Transaction type constants
|
|
386
|
-
TYPES = %w[purchase usage refund adjustment bonus].freeze
|
|
387
|
-
|
|
388
|
-
def initialize(data)
|
|
389
|
-
@id = data["id"]
|
|
390
|
-
@type = data["type"]
|
|
391
|
-
@amount = data["amount"] || 0
|
|
392
|
-
@balance_after = data["balance_after"] || data["balanceAfter"] || 0
|
|
393
|
-
@description = data["description"]
|
|
394
|
-
@message_id = data["message_id"] || data["messageId"]
|
|
395
|
-
@created_at = parse_time(data["created_at"] || data["createdAt"])
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
def credit?
|
|
399
|
-
amount > 0
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
def debit?
|
|
403
|
-
amount < 0
|
|
404
|
-
end
|
|
405
|
-
|
|
406
|
-
private
|
|
407
|
-
|
|
408
|
-
def parse_time(value)
|
|
409
|
-
return nil if value.nil?
|
|
410
|
-
Time.parse(value)
|
|
411
|
-
rescue ArgumentError
|
|
412
|
-
nil
|
|
413
|
-
end
|
|
414
|
-
end
|
|
415
|
-
|
|
416
|
-
# Represents an API key
|
|
417
|
-
class ApiKey
|
|
418
|
-
attr_reader :id, :name, :type, :prefix, :last_four, :permissions,
|
|
419
|
-
:created_at, :last_used_at, :expires_at, :is_revoked
|
|
420
|
-
|
|
421
|
-
def initialize(data)
|
|
422
|
-
@id = data["id"]
|
|
423
|
-
@name = data["name"]
|
|
424
|
-
@type = data["type"]
|
|
425
|
-
@prefix = data["prefix"]
|
|
426
|
-
@last_four = data["last_four"] || data["lastFour"]
|
|
427
|
-
@permissions = data["permissions"] || []
|
|
428
|
-
@created_at = parse_time(data["created_at"] || data["createdAt"])
|
|
429
|
-
@last_used_at = parse_time(data["last_used_at"] || data["lastUsedAt"])
|
|
430
|
-
@expires_at = parse_time(data["expires_at"] || data["expiresAt"])
|
|
431
|
-
@is_revoked = data["is_revoked"] || data["isRevoked"] || false
|
|
432
|
-
end
|
|
433
|
-
|
|
434
|
-
def test?
|
|
435
|
-
type == "test"
|
|
436
|
-
end
|
|
437
|
-
|
|
438
|
-
def live?
|
|
439
|
-
type == "live"
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
def revoked?
|
|
443
|
-
is_revoked
|
|
444
|
-
end
|
|
445
|
-
|
|
446
|
-
private
|
|
447
|
-
|
|
448
|
-
def parse_time(value)
|
|
449
|
-
return nil if value.nil?
|
|
450
|
-
Time.parse(value)
|
|
451
|
-
rescue ArgumentError
|
|
452
|
-
nil
|
|
453
|
-
end
|
|
454
|
-
end
|
|
455
162
|
end
|
data/lib/sendly/version.rb
CHANGED
data/lib/sendly.rb
CHANGED
|
@@ -9,8 +9,6 @@ require_relative "sendly/types"
|
|
|
9
9
|
require_relative "sendly/client"
|
|
10
10
|
require_relative "sendly/messages"
|
|
11
11
|
require_relative "sendly/webhooks"
|
|
12
|
-
require_relative "sendly/webhooks_resource"
|
|
13
|
-
require_relative "sendly/account_resource"
|
|
14
12
|
|
|
15
13
|
# Sendly Ruby SDK
|
|
16
14
|
#
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sendly
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1
|
|
4
|
+
version: 1.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sendly
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: faraday
|
|
@@ -123,14 +122,12 @@ files:
|
|
|
123
122
|
- examples/list_messages.rb
|
|
124
123
|
- examples/send_sms.rb
|
|
125
124
|
- lib/sendly.rb
|
|
126
|
-
- lib/sendly/account_resource.rb
|
|
127
125
|
- lib/sendly/client.rb
|
|
128
126
|
- lib/sendly/errors.rb
|
|
129
127
|
- lib/sendly/messages.rb
|
|
130
128
|
- lib/sendly/types.rb
|
|
131
129
|
- lib/sendly/version.rb
|
|
132
130
|
- lib/sendly/webhooks.rb
|
|
133
|
-
- lib/sendly/webhooks_resource.rb
|
|
134
131
|
homepage: https://github.com/sendly-live/sendly-ruby
|
|
135
132
|
licenses:
|
|
136
133
|
- MIT
|
|
@@ -139,7 +136,6 @@ metadata:
|
|
|
139
136
|
source_code_uri: https://github.com/sendly-live/sendly-ruby
|
|
140
137
|
changelog_uri: https://github.com/sendly-live/sendly-ruby/blob/main/CHANGELOG.md
|
|
141
138
|
documentation_uri: https://sendly.live/docs
|
|
142
|
-
post_install_message:
|
|
143
139
|
rdoc_options: []
|
|
144
140
|
require_paths:
|
|
145
141
|
- lib
|
|
@@ -154,8 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
154
150
|
- !ruby/object:Gem::Version
|
|
155
151
|
version: '0'
|
|
156
152
|
requirements: []
|
|
157
|
-
rubygems_version: 3.
|
|
158
|
-
signing_key:
|
|
153
|
+
rubygems_version: 3.6.9
|
|
159
154
|
specification_version: 4
|
|
160
155
|
summary: Official Ruby SDK for the Sendly SMS API
|
|
161
156
|
test_files: []
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Sendly
|
|
4
|
-
# Account resource for accessing account information, credits, and API keys
|
|
5
|
-
class AccountResource
|
|
6
|
-
# @param client [Sendly::Client] The API client
|
|
7
|
-
def initialize(client)
|
|
8
|
-
@client = client
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
# Get account information
|
|
12
|
-
#
|
|
13
|
-
# @return [Sendly::Account]
|
|
14
|
-
def get
|
|
15
|
-
response = @client.get("/account")
|
|
16
|
-
Account.new(response)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Get credit balance
|
|
20
|
-
#
|
|
21
|
-
# @return [Sendly::Credits]
|
|
22
|
-
#
|
|
23
|
-
# @example
|
|
24
|
-
# credits = client.account.credits
|
|
25
|
-
# puts "Available: #{credits.available_balance} credits"
|
|
26
|
-
def credits
|
|
27
|
-
response = @client.get("/credits")
|
|
28
|
-
Credits.new(response)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Get credit transaction history
|
|
32
|
-
#
|
|
33
|
-
# @param limit [Integer, nil] Maximum number of transactions to return
|
|
34
|
-
# @param offset [Integer, nil] Number of transactions to skip
|
|
35
|
-
# @return [Array<Sendly::CreditTransaction>]
|
|
36
|
-
def transactions(limit: nil, offset: nil)
|
|
37
|
-
params = {}
|
|
38
|
-
params[:limit] = limit if limit
|
|
39
|
-
params[:offset] = offset if offset
|
|
40
|
-
|
|
41
|
-
response = @client.get("/credits/transactions", params)
|
|
42
|
-
response.map { |data| CreditTransaction.new(data) }
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# List API keys for the account
|
|
46
|
-
#
|
|
47
|
-
# @return [Array<Sendly::ApiKey>]
|
|
48
|
-
def api_keys
|
|
49
|
-
response = @client.get("/keys")
|
|
50
|
-
response.map { |data| ApiKey.new(data) }
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Get a specific API key by ID
|
|
54
|
-
#
|
|
55
|
-
# @param key_id [String] API key ID
|
|
56
|
-
# @return [Sendly::ApiKey]
|
|
57
|
-
def api_key(key_id)
|
|
58
|
-
response = @client.get("/keys/#{key_id}")
|
|
59
|
-
ApiKey.new(response)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Get usage statistics for an API key
|
|
63
|
-
#
|
|
64
|
-
# @param key_id [String] API key ID
|
|
65
|
-
# @return [Hash] Usage statistics
|
|
66
|
-
def api_key_usage(key_id)
|
|
67
|
-
@client.get("/keys/#{key_id}/usage")
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Sendly
|
|
4
|
-
# Webhooks resource for managing webhook endpoints
|
|
5
|
-
class WebhooksResource
|
|
6
|
-
# @param client [Sendly::Client] The API client
|
|
7
|
-
def initialize(client)
|
|
8
|
-
@client = client
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
# Create a new webhook endpoint
|
|
12
|
-
#
|
|
13
|
-
# @param url [String] HTTPS endpoint URL
|
|
14
|
-
# @param events [Array<String>] Event types to subscribe to
|
|
15
|
-
# @param description [String, nil] Optional description
|
|
16
|
-
# @param metadata [Hash, nil] Custom metadata
|
|
17
|
-
# @return [Sendly::WebhookCreatedResponse]
|
|
18
|
-
#
|
|
19
|
-
# @example
|
|
20
|
-
# webhook = client.webhooks.create(
|
|
21
|
-
# url: "https://example.com/webhooks",
|
|
22
|
-
# events: ["message.delivered", "message.failed"]
|
|
23
|
-
# )
|
|
24
|
-
# puts "Secret: #{webhook.secret}" # Save this - only shown once!
|
|
25
|
-
def create(url:, events:, description: nil, metadata: nil)
|
|
26
|
-
raise ArgumentError, "Webhook URL must be HTTPS" unless url&.start_with?("https://")
|
|
27
|
-
raise ArgumentError, "At least one event type is required" if events.nil? || events.empty?
|
|
28
|
-
|
|
29
|
-
body = { url: url, events: events }
|
|
30
|
-
body[:description] = description if description
|
|
31
|
-
body[:metadata] = metadata if metadata
|
|
32
|
-
|
|
33
|
-
response = @client.post("/webhooks", body)
|
|
34
|
-
WebhookCreatedResponse.new(response)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# List all webhooks
|
|
38
|
-
#
|
|
39
|
-
# @return [Array<Sendly::Webhook>]
|
|
40
|
-
def list
|
|
41
|
-
response = @client.get("/webhooks")
|
|
42
|
-
response.map { |data| Webhook.new(data) }
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Get a specific webhook by ID
|
|
46
|
-
#
|
|
47
|
-
# @param webhook_id [String] Webhook ID (whk_xxx)
|
|
48
|
-
# @return [Sendly::Webhook]
|
|
49
|
-
def get(webhook_id)
|
|
50
|
-
validate_webhook_id!(webhook_id)
|
|
51
|
-
response = @client.get("/webhooks/#{webhook_id}")
|
|
52
|
-
Webhook.new(response)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Update a webhook configuration
|
|
56
|
-
#
|
|
57
|
-
# @param webhook_id [String] Webhook ID
|
|
58
|
-
# @param url [String, nil] New URL
|
|
59
|
-
# @param events [Array<String>, nil] New event subscriptions
|
|
60
|
-
# @param description [String, nil] New description
|
|
61
|
-
# @param is_active [Boolean, nil] Enable/disable webhook
|
|
62
|
-
# @param metadata [Hash, nil] Custom metadata
|
|
63
|
-
# @return [Sendly::Webhook]
|
|
64
|
-
def update(webhook_id, url: nil, events: nil, description: nil, is_active: nil, metadata: nil)
|
|
65
|
-
validate_webhook_id!(webhook_id)
|
|
66
|
-
raise ArgumentError, "Webhook URL must be HTTPS" if url && !url.start_with?("https://")
|
|
67
|
-
|
|
68
|
-
body = {}
|
|
69
|
-
body[:url] = url unless url.nil?
|
|
70
|
-
body[:events] = events unless events.nil?
|
|
71
|
-
body[:description] = description unless description.nil?
|
|
72
|
-
body[:is_active] = is_active unless is_active.nil?
|
|
73
|
-
body[:metadata] = metadata unless metadata.nil?
|
|
74
|
-
|
|
75
|
-
response = @client.patch("/webhooks/#{webhook_id}", body)
|
|
76
|
-
Webhook.new(response)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Delete a webhook
|
|
80
|
-
#
|
|
81
|
-
# @param webhook_id [String] Webhook ID
|
|
82
|
-
# @return [void]
|
|
83
|
-
def delete(webhook_id)
|
|
84
|
-
validate_webhook_id!(webhook_id)
|
|
85
|
-
@client.delete("/webhooks/#{webhook_id}")
|
|
86
|
-
nil
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Test a webhook endpoint
|
|
90
|
-
#
|
|
91
|
-
# @param webhook_id [String] Webhook ID
|
|
92
|
-
# @return [Sendly::WebhookTestResult]
|
|
93
|
-
def test(webhook_id)
|
|
94
|
-
validate_webhook_id!(webhook_id)
|
|
95
|
-
response = @client.post("/webhooks/#{webhook_id}/test")
|
|
96
|
-
WebhookTestResult.new(response)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Rotate the webhook signing secret
|
|
100
|
-
#
|
|
101
|
-
# @param webhook_id [String] Webhook ID
|
|
102
|
-
# @return [Sendly::WebhookSecretRotation]
|
|
103
|
-
def rotate_secret(webhook_id)
|
|
104
|
-
validate_webhook_id!(webhook_id)
|
|
105
|
-
response = @client.post("/webhooks/#{webhook_id}/rotate-secret")
|
|
106
|
-
WebhookSecretRotation.new(response)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Get delivery history for a webhook
|
|
110
|
-
#
|
|
111
|
-
# @param webhook_id [String] Webhook ID
|
|
112
|
-
# @return [Array<Sendly::WebhookDelivery>]
|
|
113
|
-
def deliveries(webhook_id)
|
|
114
|
-
validate_webhook_id!(webhook_id)
|
|
115
|
-
response = @client.get("/webhooks/#{webhook_id}/deliveries")
|
|
116
|
-
response.map { |data| WebhookDelivery.new(data) }
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
# Retry a failed delivery
|
|
120
|
-
#
|
|
121
|
-
# @param webhook_id [String] Webhook ID
|
|
122
|
-
# @param delivery_id [String] Delivery ID
|
|
123
|
-
# @return [void]
|
|
124
|
-
def retry_delivery(webhook_id, delivery_id)
|
|
125
|
-
validate_webhook_id!(webhook_id)
|
|
126
|
-
validate_delivery_id!(delivery_id)
|
|
127
|
-
@client.post("/webhooks/#{webhook_id}/deliveries/#{delivery_id}/retry")
|
|
128
|
-
nil
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# List available event types
|
|
132
|
-
#
|
|
133
|
-
# @return [Array<String>]
|
|
134
|
-
def event_types
|
|
135
|
-
response = @client.get("/webhooks/event-types")
|
|
136
|
-
(response["events"] || []).map { |e| e["type"] }
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
private
|
|
140
|
-
|
|
141
|
-
def validate_webhook_id!(webhook_id)
|
|
142
|
-
raise ArgumentError, "Invalid webhook ID format" unless webhook_id&.start_with?("whk_")
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def validate_delivery_id!(delivery_id)
|
|
146
|
-
raise ArgumentError, "Invalid delivery ID format" unless delivery_id&.start_with?("del_")
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
end
|