philiprehberger-webhook_builder 0.1.0 → 0.2.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 +14 -0
- data/README.md +113 -12
- data/lib/philiprehberger/webhook_builder/backoff.rb +76 -0
- data/lib/philiprehberger/webhook_builder/client.rb +73 -24
- data/lib/philiprehberger/webhook_builder/version.rb +1 -1
- data/lib/philiprehberger/webhook_builder.rb +4 -3
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9992d034a4e76ce146dae4dfba7d503b3eb27cb2bc12722655ffa1f84a24b6ab
|
|
4
|
+
data.tar.gz: 112d676f685b96e900a9a96adcf529165a346b28c587c6d1a76ac1f1fe333fc1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a4fff7580a5193f2e2260eeb6400f3dfd06173643e909e9e06a6d88960a8237a3e132ccde8a27d263215bec87e11a1966aaecf5ee8f17d0cfe422c1b45c5603f
|
|
7
|
+
data.tar.gz: 76166bc2d3ce885fc1106600864f4edfaa8b358588b00ee2982125ca7fc069a907c4a22a2babc55905180b63fef5e7de04f1e4188a9e59410e558026a5bf028b
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2026-03-29
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Batch delivery via `client.deliver_batch(events)` with configurable `concurrency:` option
|
|
14
|
+
- Retry backoff strategies: `:exponential` (default), `:linear`, `:fixed`, or custom Proc via `backoff:` option
|
|
15
|
+
- Backoff strategy classes in `Philiprehberger::WebhookBuilder::Backoff` module
|
|
16
|
+
- Header customization with per-delivery `headers:` parameter and client-level `default_headers:` option
|
|
17
|
+
- Per-delivery headers override default headers
|
|
18
|
+
|
|
19
|
+
## [0.1.1] - 2026-03-22
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Expand test coverage
|
|
23
|
+
|
|
10
24
|
## [0.1.0] - 2026-03-22
|
|
11
25
|
|
|
12
26
|
### Added
|
data/README.md
CHANGED
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/philiprehberger/rb-webhook-builder/actions/workflows/ci.yml)
|
|
4
4
|
[](https://rubygems.org/gems/philiprehberger-webhook_builder)
|
|
5
|
+
[](https://github.com/philiprehberger/rb-webhook-builder/releases)
|
|
6
|
+
[](https://github.com/philiprehberger/rb-webhook-builder/commits/main)
|
|
5
7
|
[](LICENSE)
|
|
8
|
+
[](https://github.com/philiprehberger/rb-webhook-builder/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
|
|
9
|
+
[](https://github.com/philiprehberger/rb-webhook-builder/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)
|
|
10
|
+
[](https://github.com/sponsors/philiprehberger)
|
|
6
11
|
|
|
7
12
|
Webhook delivery client with HMAC signing, retry, and tracking
|
|
8
13
|
|
|
@@ -39,20 +44,91 @@ delivery.success? # => true
|
|
|
39
44
|
delivery.response_code # => 200
|
|
40
45
|
```
|
|
41
46
|
|
|
42
|
-
###
|
|
47
|
+
### Batch Delivery
|
|
43
48
|
|
|
44
49
|
```ruby
|
|
50
|
+
require "philiprehberger/webhook_builder"
|
|
51
|
+
|
|
52
|
+
client = Philiprehberger::WebhookBuilder.new(
|
|
53
|
+
url: "https://example.com/webhooks",
|
|
54
|
+
secret: "your-signing-secret",
|
|
55
|
+
concurrency: 8
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
events = [
|
|
59
|
+
{ event: "order.created", payload: { id: 1 } },
|
|
60
|
+
{ event: "order.updated", payload: { id: 2 } },
|
|
61
|
+
{ event: "order.deleted", payload: { id: 3 } }
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
results = client.deliver_batch(events)
|
|
65
|
+
results.each { |d| puts "#{d.response_code}: #{d.success?}" }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Backoff Strategies
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
require "philiprehberger/webhook_builder"
|
|
72
|
+
|
|
73
|
+
# Exponential backoff (default): 1s, 2s, 4s, 8s, ...
|
|
74
|
+
client = Philiprehberger::WebhookBuilder.new(
|
|
75
|
+
url: "https://example.com/webhooks",
|
|
76
|
+
secret: "secret",
|
|
77
|
+
backoff: :exponential
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Linear backoff: 1s, 2s, 3s, 4s, ...
|
|
81
|
+
client = Philiprehberger::WebhookBuilder.new(
|
|
82
|
+
url: "https://example.com/webhooks",
|
|
83
|
+
secret: "secret",
|
|
84
|
+
backoff: :linear
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Fixed backoff: 1s, 1s, 1s, ...
|
|
88
|
+
client = Philiprehberger::WebhookBuilder.new(
|
|
89
|
+
url: "https://example.com/webhooks",
|
|
90
|
+
secret: "secret",
|
|
91
|
+
backoff: :fixed
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Custom Proc backoff
|
|
95
|
+
client = Philiprehberger::WebhookBuilder.new(
|
|
96
|
+
url: "https://example.com/webhooks",
|
|
97
|
+
secret: "secret",
|
|
98
|
+
backoff: ->(attempt) { attempt * 0.5 }
|
|
99
|
+
)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Header Customization
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
require "philiprehberger/webhook_builder"
|
|
106
|
+
|
|
107
|
+
# Default headers on all deliveries
|
|
45
108
|
client = Philiprehberger::WebhookBuilder.new(
|
|
46
|
-
url: "https://
|
|
47
|
-
secret: "
|
|
48
|
-
|
|
49
|
-
|
|
109
|
+
url: "https://example.com/webhooks",
|
|
110
|
+
secret: "secret",
|
|
111
|
+
default_headers: { "X-Tenant" => "acme" }
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Per-delivery headers (override defaults)
|
|
115
|
+
client.deliver(
|
|
116
|
+
event: "order.created",
|
|
117
|
+
payload: { id: 1 },
|
|
118
|
+
headers: { "X-Priority" => "high" }
|
|
50
119
|
)
|
|
51
120
|
```
|
|
52
121
|
|
|
53
122
|
### Delivery Tracking
|
|
54
123
|
|
|
55
124
|
```ruby
|
|
125
|
+
require "philiprehberger/webhook_builder"
|
|
126
|
+
|
|
127
|
+
client = Philiprehberger::WebhookBuilder.new(
|
|
128
|
+
url: "https://example.com/webhooks",
|
|
129
|
+
secret: "secret"
|
|
130
|
+
)
|
|
131
|
+
|
|
56
132
|
delivery = client.deliver(event: "user.updated", payload: { id: 42 })
|
|
57
133
|
|
|
58
134
|
delivery.success? # => true/false
|
|
@@ -63,18 +139,15 @@ delivery.response_body # => '{"ok":true}'
|
|
|
63
139
|
delivery.error # => nil or error message
|
|
64
140
|
```
|
|
65
141
|
|
|
66
|
-
### HMAC Signing
|
|
67
|
-
|
|
68
|
-
Every request includes an `X-Webhook-Signature` header with an HMAC-SHA256 hex digest of the JSON body, signed with the configured secret. The `X-Webhook-Event` header contains the event type.
|
|
69
|
-
|
|
70
142
|
## API
|
|
71
143
|
|
|
72
144
|
### `Client`
|
|
73
145
|
|
|
74
146
|
| Method | Description |
|
|
75
147
|
|--------|-------------|
|
|
76
|
-
| `.new(url:, secret:, timeout:, max_retries:)` | Create a webhook client
|
|
77
|
-
| `#deliver(event:, payload:)` | Deliver a webhook event and return a Delivery |
|
|
148
|
+
| `.new(url:, secret:, timeout:, max_retries:, backoff:, concurrency:, default_headers:)` | Create a webhook client |
|
|
149
|
+
| `#deliver(event:, payload:, headers:)` | Deliver a webhook event and return a Delivery |
|
|
150
|
+
| `#deliver_batch(events)` | Deliver multiple events concurrently and return an array of Delivery results |
|
|
78
151
|
|
|
79
152
|
### `Delivery`
|
|
80
153
|
|
|
@@ -87,6 +160,27 @@ Every request includes an `X-Webhook-Signature` header with an HMAC-SHA256 hex d
|
|
|
87
160
|
| `#response_body` | The response body string |
|
|
88
161
|
| `#error` | Error message if delivery failed |
|
|
89
162
|
|
|
163
|
+
### `Backoff::Exponential`
|
|
164
|
+
|
|
165
|
+
| Method | Description |
|
|
166
|
+
|--------|-------------|
|
|
167
|
+
| `.new(base:, max_delay:, jitter:)` | Create exponential strategy (defaults: base=1, max_delay=30, jitter=false) |
|
|
168
|
+
| `#call(attempt)` | Calculate delay for given attempt |
|
|
169
|
+
|
|
170
|
+
### `Backoff::Linear`
|
|
171
|
+
|
|
172
|
+
| Method | Description |
|
|
173
|
+
|--------|-------------|
|
|
174
|
+
| `.new(base:, max_delay:)` | Create linear strategy (defaults: base=1, max_delay=30) |
|
|
175
|
+
| `#call(attempt)` | Calculate delay for given attempt |
|
|
176
|
+
|
|
177
|
+
### `Backoff::Fixed`
|
|
178
|
+
|
|
179
|
+
| Method | Description |
|
|
180
|
+
|--------|-------------|
|
|
181
|
+
| `.new(delay:)` | Create fixed strategy (default: delay=1) |
|
|
182
|
+
| `#call(attempt)` | Returns constant delay |
|
|
183
|
+
|
|
90
184
|
## Development
|
|
91
185
|
|
|
92
186
|
```bash
|
|
@@ -95,6 +189,13 @@ bundle exec rspec
|
|
|
95
189
|
bundle exec rubocop
|
|
96
190
|
```
|
|
97
191
|
|
|
192
|
+
## Support
|
|
193
|
+
|
|
194
|
+
If you find this package useful, consider giving it a star on GitHub — it helps motivate continued maintenance and development.
|
|
195
|
+
|
|
196
|
+
[](https://www.linkedin.com/in/philiprehberger)
|
|
197
|
+
[](https://philiprehberger.com/open-source-packages)
|
|
198
|
+
|
|
98
199
|
## License
|
|
99
200
|
|
|
100
|
-
MIT
|
|
201
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Philiprehberger
|
|
4
|
+
module WebhookBuilder
|
|
5
|
+
module Backoff
|
|
6
|
+
# Exponential backoff: base * 2^attempt, capped at max_delay.
|
|
7
|
+
class Exponential
|
|
8
|
+
# @param base [Numeric] base delay in seconds (default: 1)
|
|
9
|
+
# @param max_delay [Numeric] maximum delay in seconds (default: 30)
|
|
10
|
+
# @param jitter [Boolean] whether to add random jitter (default: false)
|
|
11
|
+
def initialize(base: 1, max_delay: 30, jitter: false)
|
|
12
|
+
@base = base
|
|
13
|
+
@max_delay = max_delay
|
|
14
|
+
@jitter = jitter
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @param attempt [Integer] the current attempt number (1-based)
|
|
18
|
+
# @return [Float] delay in seconds
|
|
19
|
+
def call(attempt)
|
|
20
|
+
delay = [@base * (2**(attempt - 1)), @max_delay].min.to_f
|
|
21
|
+
delay *= rand if @jitter
|
|
22
|
+
delay
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Linear backoff: base * attempt, capped at max_delay.
|
|
27
|
+
class Linear
|
|
28
|
+
# @param base [Numeric] base delay in seconds (default: 1)
|
|
29
|
+
# @param max_delay [Numeric] maximum delay in seconds (default: 30)
|
|
30
|
+
def initialize(base: 1, max_delay: 30)
|
|
31
|
+
@base = base
|
|
32
|
+
@max_delay = max_delay
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @param attempt [Integer] the current attempt number (1-based)
|
|
36
|
+
# @return [Float] delay in seconds
|
|
37
|
+
def call(attempt)
|
|
38
|
+
[@base * attempt, @max_delay].min.to_f
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Fixed backoff: constant delay.
|
|
43
|
+
class Fixed
|
|
44
|
+
# @param delay [Numeric] delay in seconds (default: 1)
|
|
45
|
+
def initialize(delay: 1)
|
|
46
|
+
@delay = delay
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @param _attempt [Integer] ignored
|
|
50
|
+
# @return [Float] delay in seconds
|
|
51
|
+
def call(_attempt)
|
|
52
|
+
@delay.to_f
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Resolve a backoff option into a callable strategy.
|
|
57
|
+
#
|
|
58
|
+
# @param option [Symbol, Proc, nil] the backoff strategy
|
|
59
|
+
# @return [#call] a callable backoff strategy
|
|
60
|
+
def self.resolve(option)
|
|
61
|
+
case option
|
|
62
|
+
when :exponential, nil
|
|
63
|
+
Exponential.new
|
|
64
|
+
when :linear
|
|
65
|
+
Linear.new
|
|
66
|
+
when :fixed
|
|
67
|
+
Fixed.new
|
|
68
|
+
when Proc
|
|
69
|
+
option
|
|
70
|
+
else
|
|
71
|
+
raise ArgumentError, "Unknown backoff strategy: #{option.inspect}. Use :exponential, :linear, :fixed, or a Proc."
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'json'
|
|
6
|
+
require 'openssl'
|
|
7
|
+
require 'time'
|
|
8
8
|
|
|
9
9
|
module Philiprehberger
|
|
10
10
|
module WebhookBuilder
|
|
@@ -19,27 +19,42 @@ module Philiprehberger
|
|
|
19
19
|
# @return [Integer] the maximum number of delivery attempts
|
|
20
20
|
attr_reader :max_retries
|
|
21
21
|
|
|
22
|
+
# @return [Integer] the maximum number of concurrent batch deliveries
|
|
23
|
+
attr_reader :concurrency
|
|
24
|
+
|
|
25
|
+
# @return [Hash] default headers included in every delivery
|
|
26
|
+
attr_reader :default_headers
|
|
27
|
+
|
|
22
28
|
# Create a new webhook client.
|
|
23
29
|
#
|
|
24
30
|
# @param url [String] the webhook endpoint URL
|
|
25
31
|
# @param secret [String] the HMAC-SHA256 signing secret
|
|
26
32
|
# @param timeout [Integer] HTTP timeout in seconds (default: 30)
|
|
27
33
|
# @param max_retries [Integer] maximum retry attempts on failure (default: 3)
|
|
28
|
-
|
|
34
|
+
# @param backoff [Symbol, Proc] backoff strategy — :exponential (default), :linear, :fixed, or a Proc
|
|
35
|
+
# @param concurrency [Integer] maximum concurrent threads for batch delivery (default: 4)
|
|
36
|
+
# @param default_headers [Hash] headers to include in every delivery
|
|
37
|
+
def initialize(url:, secret:, timeout: 30, max_retries: 3, backoff: :exponential, concurrency: 4,
|
|
38
|
+
default_headers: {})
|
|
29
39
|
@url = url
|
|
30
40
|
@secret = secret
|
|
31
41
|
@timeout = timeout
|
|
32
42
|
@max_retries = max_retries
|
|
43
|
+
@backoff_strategy = Backoff.resolve(backoff)
|
|
44
|
+
@concurrency = concurrency
|
|
45
|
+
@default_headers = default_headers.dup.freeze
|
|
33
46
|
end
|
|
34
47
|
|
|
35
48
|
# Deliver a webhook event.
|
|
36
49
|
#
|
|
37
50
|
# @param event [String] the event type (e.g., "order.created")
|
|
38
51
|
# @param payload [Hash] the event payload
|
|
52
|
+
# @param headers [Hash] per-delivery headers (override default_headers)
|
|
39
53
|
# @return [Delivery] the delivery result
|
|
40
|
-
def deliver(event:, payload:)
|
|
54
|
+
def deliver(event:, payload:, headers: {})
|
|
41
55
|
body = JSON.generate({ event: event, payload: payload, timestamp: Time.now.utc.iso8601 })
|
|
42
56
|
signature = sign(body)
|
|
57
|
+
merged_headers = @default_headers.merge(headers)
|
|
43
58
|
|
|
44
59
|
attempts = 0
|
|
45
60
|
start_time = monotonic_now
|
|
@@ -50,7 +65,7 @@ module Philiprehberger
|
|
|
50
65
|
loop do
|
|
51
66
|
attempts += 1
|
|
52
67
|
begin
|
|
53
|
-
response = send_request(body, signature, event)
|
|
68
|
+
response = send_request(body, signature, event, merged_headers)
|
|
54
69
|
last_response_code = response.code.to_i
|
|
55
70
|
last_response_body = response.body
|
|
56
71
|
|
|
@@ -71,7 +86,7 @@ module Philiprehberger
|
|
|
71
86
|
|
|
72
87
|
break if attempts > @max_retries
|
|
73
88
|
|
|
74
|
-
sleep(
|
|
89
|
+
sleep(@backoff_strategy.call(attempts))
|
|
75
90
|
end
|
|
76
91
|
|
|
77
92
|
Delivery.new(
|
|
@@ -84,6 +99,44 @@ module Philiprehberger
|
|
|
84
99
|
)
|
|
85
100
|
end
|
|
86
101
|
|
|
102
|
+
# Deliver multiple webhook events concurrently.
|
|
103
|
+
#
|
|
104
|
+
# @param events [Array<Hash>] array of { event:, payload: } hashes, optionally with headers:
|
|
105
|
+
# @return [Array<Delivery>] delivery results in the same order as input
|
|
106
|
+
def deliver_batch(events)
|
|
107
|
+
results = Array.new(events.length)
|
|
108
|
+
mutex = Mutex.new
|
|
109
|
+
queue = Queue.new
|
|
110
|
+
|
|
111
|
+
events.each_with_index do |item, index|
|
|
112
|
+
queue << [item, index]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
threads = Array.new([@concurrency, events.length].min) do
|
|
116
|
+
Thread.new do
|
|
117
|
+
loop do
|
|
118
|
+
pair = begin
|
|
119
|
+
queue.pop(true)
|
|
120
|
+
rescue ThreadError
|
|
121
|
+
nil
|
|
122
|
+
end
|
|
123
|
+
break unless pair
|
|
124
|
+
|
|
125
|
+
item, index = pair
|
|
126
|
+
delivery = deliver(
|
|
127
|
+
event: item[:event],
|
|
128
|
+
payload: item[:payload],
|
|
129
|
+
headers: item.fetch(:headers, {})
|
|
130
|
+
)
|
|
131
|
+
mutex.synchronize { results[index] = delivery }
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
threads.each(&:join)
|
|
137
|
+
results
|
|
138
|
+
end
|
|
139
|
+
|
|
87
140
|
private
|
|
88
141
|
|
|
89
142
|
# Sign the request body with HMAC-SHA256.
|
|
@@ -91,7 +144,7 @@ module Philiprehberger
|
|
|
91
144
|
# @param body [String] the JSON body
|
|
92
145
|
# @return [String] the hex-encoded HMAC signature
|
|
93
146
|
def sign(body)
|
|
94
|
-
OpenSSL::HMAC.hexdigest(
|
|
147
|
+
OpenSSL::HMAC.hexdigest('SHA256', @secret, body)
|
|
95
148
|
end
|
|
96
149
|
|
|
97
150
|
# Send the HTTP POST request.
|
|
@@ -99,32 +152,28 @@ module Philiprehberger
|
|
|
99
152
|
# @param body [String] the JSON body
|
|
100
153
|
# @param signature [String] the HMAC signature
|
|
101
154
|
# @param event [String] the event type
|
|
155
|
+
# @param extra_headers [Hash] additional headers to include
|
|
102
156
|
# @return [Net::HTTPResponse]
|
|
103
|
-
def send_request(body, signature, event)
|
|
157
|
+
def send_request(body, signature, event, extra_headers = {})
|
|
104
158
|
uri = URI.parse(@url)
|
|
105
159
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
106
|
-
http.use_ssl = uri.scheme ==
|
|
160
|
+
http.use_ssl = uri.scheme == 'https'
|
|
107
161
|
http.open_timeout = @timeout
|
|
108
162
|
http.read_timeout = @timeout
|
|
109
163
|
|
|
110
164
|
request = Net::HTTP::Post.new(uri.request_uri)
|
|
111
|
-
request[
|
|
112
|
-
request[
|
|
113
|
-
request[
|
|
114
|
-
request[
|
|
165
|
+
request['Content-Type'] = 'application/json'
|
|
166
|
+
request['X-Webhook-Signature'] = signature
|
|
167
|
+
request['X-Webhook-Event'] = event
|
|
168
|
+
request['User-Agent'] = "philiprehberger-webhook_builder/#{VERSION}"
|
|
169
|
+
|
|
170
|
+
extra_headers.each { |key, value| request[key] = value }
|
|
171
|
+
|
|
115
172
|
request.body = body
|
|
116
173
|
|
|
117
174
|
http.request(request)
|
|
118
175
|
end
|
|
119
176
|
|
|
120
|
-
# Calculate exponential backoff delay.
|
|
121
|
-
#
|
|
122
|
-
# @param attempt [Integer] the current attempt number
|
|
123
|
-
# @return [Float] delay in seconds
|
|
124
|
-
def backoff_delay(attempt)
|
|
125
|
-
[2**(attempt - 1), 30].min
|
|
126
|
-
end
|
|
127
|
-
|
|
128
177
|
def monotonic_now
|
|
129
178
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
130
179
|
end
|
|
@@ -15,6 +15,7 @@ module Philiprehberger
|
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
require_relative
|
|
19
|
-
require_relative
|
|
20
|
-
require_relative
|
|
18
|
+
require_relative 'webhook_builder/version'
|
|
19
|
+
require_relative 'webhook_builder/delivery'
|
|
20
|
+
require_relative 'webhook_builder/backoff'
|
|
21
|
+
require_relative 'webhook_builder/client'
|
metadata
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-webhook_builder
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A webhook delivery client that signs payloads with HMAC-SHA256, retries
|
|
14
|
-
failed deliveries with
|
|
15
|
-
response codes, attempts, and
|
|
14
|
+
failed deliveries with configurable backoff strategies, supports batch delivery,
|
|
15
|
+
custom headers, and tracks delivery status including response codes, attempts, and
|
|
16
|
+
duration.
|
|
16
17
|
email:
|
|
17
18
|
- me@philiprehberger.com
|
|
18
19
|
executables: []
|
|
@@ -23,6 +24,7 @@ files:
|
|
|
23
24
|
- LICENSE
|
|
24
25
|
- README.md
|
|
25
26
|
- lib/philiprehberger/webhook_builder.rb
|
|
27
|
+
- lib/philiprehberger/webhook_builder/backoff.rb
|
|
26
28
|
- lib/philiprehberger/webhook_builder/client.rb
|
|
27
29
|
- lib/philiprehberger/webhook_builder/delivery.rb
|
|
28
30
|
- lib/philiprehberger/webhook_builder/version.rb
|