hookbridge 1.0.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +74 -201
- data/lib/hookbridge/client.rb +372 -179
- data/lib/hookbridge/types.rb +257 -223
- data/lib/hookbridge/version.rb +1 -1
- 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: 21217d26b8b60f7f11e90921ec65c85886cb9db5083201c54271ab3b73af67c3
|
|
4
|
+
data.tar.gz: 5c35bcaced137cce82f7763a4c9d338155a4b3324f9ce12ba56455a6586ada29
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb9042d203744bdc6714a8385aa22eb4bc4df4633f5394c56b9cf11165c7abe4c8da7d49a89e24f32a30238575eb06e1afed085ea388128ffde9a00f0e15b72e
|
|
7
|
+
data.tar.gz: 7a0e7be1de066280224cc326218ee8fe5a88d141f2d500e78b24eabe413e961a2272cfdd3c47ffea9cd51815a9310096b0b263c2b5c66bde4ddbee713b34b38c
|
data/README.md
CHANGED
|
@@ -1,254 +1,127 @@
|
|
|
1
1
|
# HookBridge Ruby SDK
|
|
2
2
|
|
|
3
|
-
Official Ruby
|
|
3
|
+
Official Ruby SDK for HookBridge. Send webhooks with guaranteed delivery, automatic retries, and inbound/outbound observability.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
Add
|
|
7
|
+
Add the gem to your `Gemfile`:
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
|
-
gem
|
|
10
|
+
gem "hookbridge"
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Then install dependencies:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
bundle install
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
Or install it yourself:
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
gem install hookbridge
|
|
23
|
-
```
|
|
24
|
-
|
|
25
19
|
## Quick Start
|
|
26
20
|
|
|
27
21
|
```ruby
|
|
28
|
-
require
|
|
29
|
-
|
|
30
|
-
# Initialize the client
|
|
31
|
-
client = HookBridge.new(api_key: 'hb_live_xxxxxxxxxxxxxxxxxxxx')
|
|
32
|
-
|
|
33
|
-
# Send a webhook
|
|
34
|
-
response = client.send(
|
|
35
|
-
endpoint: 'my-endpoint',
|
|
36
|
-
payload: { event: 'user.created', data: { id: 123 } }
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
puts "Message queued: #{response.message_id}"
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
## Configuration
|
|
43
|
-
|
|
44
|
-
```ruby
|
|
45
|
-
client = HookBridge.new(
|
|
46
|
-
api_key: 'hb_live_xxxxxxxxxxxxxxxxxxxx',
|
|
47
|
-
base_url: 'https://api.hookbridge.io', # optional, can also use HOOKBRIDGE_BASE_URL env var
|
|
48
|
-
timeout: 30, # request timeout in seconds (default: 30)
|
|
49
|
-
retries: 3 # number of retries for failed requests (default: 3)
|
|
50
|
-
)
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Usage
|
|
22
|
+
require "hookbridge"
|
|
54
23
|
|
|
55
|
-
|
|
24
|
+
client = HookBridge.new(api_key: "hb_live_xxxxxxxxxxxxxxxxxxxx")
|
|
56
25
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
endpoint: 'my-endpoint',
|
|
61
|
-
payload: { event: 'order.completed', order_id: 456 }
|
|
26
|
+
endpoint = client.create_endpoint(
|
|
27
|
+
url: "https://customer.app/webhooks",
|
|
28
|
+
description: "Customer production webhook"
|
|
62
29
|
)
|
|
63
30
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
payload: { event: 'payment.processed' },
|
|
68
|
-
idempotency_key: 'payment_789'
|
|
31
|
+
result = client.send(
|
|
32
|
+
endpoint_id: endpoint.id,
|
|
33
|
+
payload: { event: "order.created" }
|
|
69
34
|
)
|
|
70
35
|
|
|
71
|
-
|
|
72
|
-
response = client.send(
|
|
73
|
-
endpoint: 'my-endpoint',
|
|
74
|
-
payload: '<xml>data</xml>',
|
|
75
|
-
content_type: 'application/xml'
|
|
76
|
-
)
|
|
36
|
+
puts result.message_id
|
|
77
37
|
```
|
|
78
38
|
|
|
79
|
-
|
|
39
|
+
## Outbound Endpoints
|
|
80
40
|
|
|
81
41
|
```ruby
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Replaying Messages
|
|
91
|
-
|
|
92
|
-
```ruby
|
|
93
|
-
# Replay a failed message
|
|
94
|
-
response = client.replay('msg_01234567890abcdef')
|
|
95
|
-
puts "Replayed, attempt #{response.attempt_count}"
|
|
96
|
-
|
|
97
|
-
# Trigger immediate retry for a pending message
|
|
98
|
-
client.retry_now('msg_01234567890abcdef')
|
|
42
|
+
endpoint = client.create_endpoint(
|
|
43
|
+
url: "https://customer.app/webhooks",
|
|
44
|
+
description: "Main production webhook",
|
|
45
|
+
rate_limit_rps: 10,
|
|
46
|
+
burst: 20
|
|
47
|
+
)
|
|
99
48
|
|
|
100
|
-
|
|
101
|
-
client.
|
|
49
|
+
details = client.get_endpoint(endpoint.id)
|
|
50
|
+
list = client.list_endpoints(limit: 50)
|
|
51
|
+
rotated = client.rotate_endpoint_secret(endpoint.id)
|
|
52
|
+
client.delete_endpoint(endpoint.id)
|
|
102
53
|
```
|
|
103
54
|
|
|
104
|
-
|
|
55
|
+
## Sending and Observability
|
|
105
56
|
|
|
106
57
|
```ruby
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
puts "#{msg.message_id}: #{msg.status}"
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# Filter by status
|
|
115
|
-
logs = client.get_logs(status: HookBridge::MessageStatus::FAILED_PERMANENT)
|
|
116
|
-
|
|
117
|
-
# Filter by time range
|
|
118
|
-
logs = client.get_logs(
|
|
119
|
-
start_time: Time.now - 3600, # last hour
|
|
120
|
-
end_time: Time.now,
|
|
121
|
-
limit: 50
|
|
58
|
+
result = client.send(
|
|
59
|
+
endpoint_id: endpoint.id,
|
|
60
|
+
payload: { event: "user.created", user_id: "usr_123" },
|
|
61
|
+
idempotency_key: "user-123-created"
|
|
122
62
|
)
|
|
123
63
|
|
|
124
|
-
|
|
125
|
-
logs = client.get_logs(limit:
|
|
126
|
-
if logs.has_more
|
|
127
|
-
next_page = client.get_logs(cursor: logs.next_cursor)
|
|
128
|
-
end
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### Getting Metrics
|
|
132
|
-
|
|
133
|
-
```ruby
|
|
134
|
-
# Get 24-hour metrics (default)
|
|
64
|
+
message = client.get_message(result.message_id)
|
|
65
|
+
logs = client.get_logs(limit: 100)
|
|
135
66
|
metrics = client.get_metrics
|
|
67
|
+
timeseries = client.get_timeseries_metrics(endpoint_id: endpoint.id)
|
|
136
68
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
puts "Avg latency: #{metrics.avg_latency_ms}ms"
|
|
140
|
-
|
|
141
|
-
# Different time windows
|
|
142
|
-
metrics_1h = client.get_metrics(window: HookBridge::MetricsWindow::ONE_HOUR)
|
|
143
|
-
metrics_7d = client.get_metrics(window: HookBridge::MetricsWindow::SEVEN_DAYS)
|
|
144
|
-
metrics_30d = client.get_metrics(window: HookBridge::MetricsWindow::THIRTY_DAYS)
|
|
69
|
+
client.replay(message.id)
|
|
70
|
+
client.replay_all_messages(status: "failed_permanent", endpoint_id: endpoint.id, limit: 50)
|
|
145
71
|
```
|
|
146
72
|
|
|
147
|
-
|
|
73
|
+
## Inbound Webhooks
|
|
148
74
|
|
|
149
75
|
```ruby
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
76
|
+
inbound = client.create_inbound_endpoint(
|
|
77
|
+
url: "https://myapp.com/webhooks/inbound",
|
|
78
|
+
name: "Stripe inbound",
|
|
79
|
+
description: "Receives Stripe events through HookBridge",
|
|
80
|
+
verify_static_token: true,
|
|
81
|
+
token_header_name: "X-Webhook-Token",
|
|
82
|
+
token_value: "my-shared-secret",
|
|
83
|
+
signing_enabled: true,
|
|
84
|
+
idempotency_header_names: ["X-Idempotency-Key"],
|
|
85
|
+
ingest_response_code: 202
|
|
86
|
+
)
|
|
156
87
|
|
|
157
|
-
#
|
|
158
|
-
|
|
159
|
-
```
|
|
88
|
+
puts inbound.ingest_url # Save this
|
|
89
|
+
puts inbound.secret_token # Only shown once
|
|
160
90
|
|
|
161
|
-
|
|
91
|
+
details = client.get_inbound_endpoint(inbound.id)
|
|
92
|
+
client.pause_inbound_endpoint(inbound.id)
|
|
93
|
+
client.resume_inbound_endpoint(inbound.id)
|
|
162
94
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
# Create a new key
|
|
171
|
-
new_key = client.create_api_key(name: 'Production Server', mode: HookBridge::APIKeyMode::LIVE)
|
|
172
|
-
puts "Save this key: #{new_key.key}" # Only shown once!
|
|
173
|
-
|
|
174
|
-
# Delete a key
|
|
175
|
-
client.delete_api_key('key_01234567890abcdef')
|
|
95
|
+
client.update_inbound_endpoint(
|
|
96
|
+
inbound.id,
|
|
97
|
+
verify_hmac: true,
|
|
98
|
+
hmac_header_name: "X-Signature",
|
|
99
|
+
hmac_secret: "whsec_inbound_secret"
|
|
100
|
+
)
|
|
176
101
|
```
|
|
177
102
|
|
|
178
|
-
##
|
|
179
|
-
|
|
180
|
-
The SDK raises specific exceptions for different error conditions:
|
|
103
|
+
## Inbound Observability
|
|
181
104
|
|
|
182
105
|
```ruby
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
rescue HookBridge::ValidationError => e
|
|
189
|
-
# Invalid request parameters (400)
|
|
190
|
-
puts "Validation failed: #{e.message}"
|
|
191
|
-
rescue HookBridge::NotFoundError => e
|
|
192
|
-
# Resource not found (404)
|
|
193
|
-
puts "Not found: #{e.message}"
|
|
194
|
-
rescue HookBridge::RateLimitError => e
|
|
195
|
-
# Rate limit exceeded (429)
|
|
196
|
-
puts "Rate limited, retry after #{e.retry_after} seconds"
|
|
197
|
-
rescue HookBridge::IdempotencyError => e
|
|
198
|
-
# Idempotency key conflict (409)
|
|
199
|
-
puts "Duplicate request: #{e.message}"
|
|
200
|
-
rescue HookBridge::ReplayLimitError => e
|
|
201
|
-
# Replay limit exceeded (409)
|
|
202
|
-
puts "Replay limit reached: #{e.message}"
|
|
203
|
-
rescue HookBridge::TimeoutError => e
|
|
204
|
-
# Request timed out
|
|
205
|
-
puts "Timeout: #{e.message}"
|
|
206
|
-
rescue HookBridge::NetworkError => e
|
|
207
|
-
# Network/connection error
|
|
208
|
-
puts "Network error: #{e.message}"
|
|
209
|
-
rescue HookBridge::Error => e
|
|
210
|
-
# Other API errors
|
|
211
|
-
puts "Error: #{e.message} (code: #{e.code}, request_id: #{e.request_id})"
|
|
212
|
-
end
|
|
106
|
+
inbound_endpoints = client.list_inbound_endpoints(limit: 50)
|
|
107
|
+
inbound_logs = client.get_inbound_logs(inbound_endpoint_id: inbound.id, limit: 50)
|
|
108
|
+
inbound_metrics = client.get_inbound_metrics(inbound_endpoint_id: inbound.id)
|
|
109
|
+
inbound_timeseries = client.get_inbound_timeseries_metrics(inbound_endpoint_id: inbound.id)
|
|
110
|
+
rejections = client.list_inbound_rejections(inbound_endpoint_id: inbound.id, limit: 25)
|
|
213
111
|
```
|
|
214
112
|
|
|
215
|
-
##
|
|
216
|
-
|
|
217
|
-
Messages can be in one of these statuses:
|
|
218
|
-
|
|
219
|
-
| Status | Description |
|
|
220
|
-
|--------|-------------|
|
|
221
|
-
| `queued` | Message is queued for delivery |
|
|
222
|
-
| `delivering` | Delivery attempt in progress |
|
|
223
|
-
| `succeeded` | Successfully delivered |
|
|
224
|
-
| `pending_retry` | Failed, will retry automatically |
|
|
225
|
-
| `failed_permanent` | Failed permanently (in DLQ) |
|
|
226
|
-
|
|
227
|
-
Use the constants:
|
|
113
|
+
## Billing and Exports
|
|
228
114
|
|
|
229
115
|
```ruby
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
- Ruby 3.0 or higher
|
|
240
|
-
- Faraday 2.x
|
|
241
|
-
|
|
242
|
-
## Development
|
|
243
|
-
|
|
244
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
|
116
|
+
subscription = client.get_subscription
|
|
117
|
+
usage = client.get_usage_history(limit: 12, offset: 0)
|
|
118
|
+
invoices = client.get_invoices(limit: 12)
|
|
119
|
+
|
|
120
|
+
export = client.create_export(
|
|
121
|
+
start_time: Time.now.utc - 86_400,
|
|
122
|
+
end_time: Time.now.utc,
|
|
123
|
+
endpoint_id: endpoint.id
|
|
124
|
+
)
|
|
245
125
|
|
|
246
|
-
|
|
247
|
-
bundle install
|
|
248
|
-
bundle exec rspec
|
|
249
|
-
bundle exec rubocop
|
|
126
|
+
download_url = client.download_export(export.id)
|
|
250
127
|
```
|
|
251
|
-
|
|
252
|
-
## License
|
|
253
|
-
|
|
254
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|