hookd-client 1.2.2 → 1.3.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 +38 -0
- data/lib/hookd/client.rb +44 -12
- data/lib/hookd/hook.rb +10 -4
- data/lib/hookd/hook_activity.rb +30 -0
- data/lib/hookd/version.rb +1 -1
- data/lib/hookd.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eeded85d2f08978163e752d64342ca5e52fd1cf1fcaade7da0305caeb53e03b5
|
|
4
|
+
data.tar.gz: 966b9eaf4f9d25549f0473471e446628af82c8ea160e5ebbefef518aa1a6fa08
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0224226f06a4996e6034caabb6182f31d9a7848262a44bd8ed0571fe898a7706f4b95c46bdb0e5617c0bc04f948fc5dc8e5bffbb46cd20de04d9bdcacbe0b2fe
|
|
7
|
+
data.tar.gz: 88e85eab06e2d351b6231f2769104b6d6a3320ae1cb2ccf68f432dad732a689ee5f50386ca69199534a76fb09425ddfd70f7d711b03b8e4d836e2b25415784ad
|
data/README.md
CHANGED
|
@@ -130,8 +130,20 @@ hooks = client.register(count: 5)
|
|
|
130
130
|
# => [#<Hookd::Hook id="abc123" ...>, #<Hookd::Hook id="def456" ...>, ...]
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
+
**Long-lived hook (survives restarts, for stored-XSS style detection):**
|
|
134
|
+
```ruby
|
|
135
|
+
hook = client.register(ttl: '7d', metadata: { target: 'acme', field: 'profile.bio' })
|
|
136
|
+
# => #<Hookd::Hook id="abc123" ...>
|
|
137
|
+
hook.expires_at # => "2025-10-08T10:30:00Z"
|
|
138
|
+
hook.metadata # => {"target"=>"acme", "field"=>"profile.bio"}
|
|
139
|
+
```
|
|
140
|
+
|
|
133
141
|
Parameters:
|
|
134
142
|
- `count` (Integer, optional) - Number of hooks to create (default: 1)
|
|
143
|
+
- `ttl` (String, optional) - Lifetime as a Go duration (`"168h"`) or day count
|
|
144
|
+
(`"7d"`); a value above the server's ephemeral `hook_ttl` registers a durable
|
|
145
|
+
long-lived hook. Omit for an ephemeral hook.
|
|
146
|
+
- `metadata` (Hash, optional) - Stored with the hook and echoed back on poll
|
|
135
147
|
|
|
136
148
|
Returns:
|
|
137
149
|
- `Hookd::Hook` object when `count` is 1 or not specified
|
|
@@ -143,6 +155,21 @@ Raises:
|
|
|
143
155
|
- `Hookd::ServerError` - Server error (5xx)
|
|
144
156
|
- `Hookd::ConnectionError` - Connection failed
|
|
145
157
|
|
|
158
|
+
##### `#activity`
|
|
159
|
+
|
|
160
|
+
List the long-lived hooks that currently have pending interactions, so you can
|
|
161
|
+
discover which fired without polling each one; drain the details with `#poll`.
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
client.activity.each do |a|
|
|
165
|
+
puts "#{a.hook.id} fired #{a.pending_count} time(s), meta=#{a.hook.metadata}"
|
|
166
|
+
client.poll(a.hook.id)
|
|
167
|
+
end
|
|
168
|
+
# => [#<Hookd::HookActivity hook=abc123 pending=3>, ...]
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Returns: Array of `Hookd::HookActivity` (empty when none fired or long-lived is disabled)
|
|
172
|
+
|
|
146
173
|
##### `#poll(hook_id)`
|
|
147
174
|
|
|
148
175
|
Poll for interactions captured by a single hook.
|
|
@@ -233,6 +260,17 @@ Attributes:
|
|
|
233
260
|
- `http` (String) - HTTP endpoint
|
|
234
261
|
- `https` (String) - HTTPS endpoint
|
|
235
262
|
- `created_at` (String) - Creation timestamp
|
|
263
|
+
- `expires_at` (String, nil) - Expiry timestamp (long-lived hooks)
|
|
264
|
+
- `metadata` (Hash, nil) - Metadata attached at registration
|
|
265
|
+
|
|
266
|
+
#### `Hookd::HookActivity`
|
|
267
|
+
|
|
268
|
+
Represents a long-lived hook that has pending interactions (returned by `#activity`).
|
|
269
|
+
|
|
270
|
+
Attributes:
|
|
271
|
+
- `hook` (`Hookd::Hook`) - The long-lived hook that fired
|
|
272
|
+
- `pending_count` (Integer) - Number of interactions awaiting poll
|
|
273
|
+
- `last_interaction_at` (String) - Timestamp of the most recent interaction
|
|
236
274
|
|
|
237
275
|
#### `Hookd::Interaction`
|
|
238
276
|
|
data/lib/hookd/client.rb
CHANGED
|
@@ -22,25 +22,20 @@ module Hookd
|
|
|
22
22
|
|
|
23
23
|
# Register one or more hooks
|
|
24
24
|
# @param count [Integer, nil] number of hooks to register (default: 1)
|
|
25
|
+
# @param ttl [String, nil] lifetime as a Go duration ("168h") or day count
|
|
26
|
+
# ("7d"); a value above the server's ephemeral hook_ttl registers a durable
|
|
27
|
+
# long-lived hook. Omit for an ephemeral hook.
|
|
28
|
+
# @param metadata [Hash, nil] arbitrary data stored with the hook and echoed
|
|
29
|
+
# back on poll
|
|
25
30
|
# @return [Hookd::Hook, Array<Hookd::Hook>] single hook or array of hooks
|
|
26
31
|
# @raise [Hookd::AuthenticationError] if authentication fails
|
|
27
32
|
# @raise [Hookd::ServerError] if server returns 5xx
|
|
28
33
|
# @raise [Hookd::ConnectionError] if connection fails
|
|
29
34
|
# @raise [ArgumentError] if count is invalid
|
|
30
|
-
def register(count: nil)
|
|
31
|
-
body = count.nil? ? nil : { count: count }
|
|
32
|
-
|
|
35
|
+
def register(count: nil, ttl: nil, metadata: nil)
|
|
33
36
|
raise ArgumentError, 'count must be a positive integer' if count && (!count.is_a?(Integer) || count < 1)
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
# Single hook response (backward compatible)
|
|
38
|
-
return Hook.from_hash(response) if response.key?('id')
|
|
39
|
-
|
|
40
|
-
# Multiple hooks response
|
|
41
|
-
return [] if response['hooks'].nil? || response['hooks'].empty?
|
|
42
|
-
|
|
43
|
-
response['hooks'].map { |h| Hook.from_hash(h) }
|
|
38
|
+
parse_register_response(post('/register', register_body(count, ttl, metadata)))
|
|
44
39
|
end
|
|
45
40
|
|
|
46
41
|
# Poll for interactions on a hook
|
|
@@ -92,8 +87,45 @@ module Hookd
|
|
|
92
87
|
get('/metrics')
|
|
93
88
|
end
|
|
94
89
|
|
|
90
|
+
# List long-lived hooks that currently have pending interactions, so you can
|
|
91
|
+
# discover which of your long-lived hooks fired without polling each one.
|
|
92
|
+
# Drain the details with #poll. Returns an empty array when none have fired
|
|
93
|
+
# (or the server has long-lived hooks disabled).
|
|
94
|
+
# @return [Array<Hookd::HookActivity>]
|
|
95
|
+
# @raise [Hookd::AuthenticationError] if authentication fails
|
|
96
|
+
# @raise [Hookd::ServerError] if server returns 5xx
|
|
97
|
+
# @raise [Hookd::ConnectionError] if connection fails
|
|
98
|
+
def activity
|
|
99
|
+
response = get('/activity')
|
|
100
|
+
|
|
101
|
+
hooks = response['hooks']
|
|
102
|
+
return [] if hooks.nil? || hooks.empty? || !hooks.is_a?(Array)
|
|
103
|
+
|
|
104
|
+
hooks.map { |h| HookActivity.from_hash(h) }
|
|
105
|
+
rescue NoMethodError => e
|
|
106
|
+
raise Error, "Invalid response format: #{e.message}"
|
|
107
|
+
end
|
|
108
|
+
|
|
95
109
|
private
|
|
96
110
|
|
|
111
|
+
def register_body(count, ttl, metadata)
|
|
112
|
+
body = {}
|
|
113
|
+
body[:count] = count unless count.nil?
|
|
114
|
+
body[:ttl] = ttl unless ttl.nil?
|
|
115
|
+
body[:metadata] = metadata unless metadata.nil?
|
|
116
|
+
body.empty? ? nil : body
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def parse_register_response(response)
|
|
120
|
+
# Single hook response (backward compatible)
|
|
121
|
+
return Hook.from_hash(response) if response.key?('id')
|
|
122
|
+
|
|
123
|
+
# Multiple hooks response
|
|
124
|
+
return [] if response['hooks'].nil? || response['hooks'].empty?
|
|
125
|
+
|
|
126
|
+
response['hooks'].map { |h| Hook.from_hash(h) }
|
|
127
|
+
end
|
|
128
|
+
|
|
97
129
|
def get(path)
|
|
98
130
|
url = "#{@server}#{path}"
|
|
99
131
|
response = @http.get(url)
|
data/lib/hookd/hook.rb
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Hookd
|
|
4
|
-
# Represents a registered hook with DNS and HTTP endpoints
|
|
4
|
+
# Represents a registered hook with DNS and HTTP endpoints. expires_at and
|
|
5
|
+
# metadata are populated for long-lived hooks (registered with a ttl) and nil
|
|
6
|
+
# otherwise.
|
|
5
7
|
class Hook
|
|
6
|
-
attr_reader :id, :dns, :http, :https, :created_at
|
|
8
|
+
attr_reader :id, :dns, :http, :https, :created_at, :expires_at, :metadata
|
|
7
9
|
|
|
8
|
-
def initialize(id:, dns:, http:, https:, created_at:)
|
|
10
|
+
def initialize(id:, dns:, http:, https:, created_at:, expires_at: nil, metadata: nil)
|
|
9
11
|
@id = id
|
|
10
12
|
@dns = dns
|
|
11
13
|
@http = http
|
|
12
14
|
@https = https
|
|
13
15
|
@created_at = created_at
|
|
16
|
+
@expires_at = expires_at
|
|
17
|
+
@metadata = metadata
|
|
14
18
|
end
|
|
15
19
|
|
|
16
20
|
# Create a Hook from API response hash
|
|
@@ -22,7 +26,9 @@ module Hookd
|
|
|
22
26
|
dns: hash['dns'],
|
|
23
27
|
http: hash['http'],
|
|
24
28
|
https: hash['https'],
|
|
25
|
-
created_at: hash['created_at']
|
|
29
|
+
created_at: hash['created_at'],
|
|
30
|
+
expires_at: hash['expires_at'],
|
|
31
|
+
metadata: hash['metadata']
|
|
26
32
|
)
|
|
27
33
|
end
|
|
28
34
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hookd
|
|
4
|
+
# Summarises a long-lived hook that currently has pending interactions,
|
|
5
|
+
# returned by Client#activity.
|
|
6
|
+
class HookActivity
|
|
7
|
+
attr_reader :hook, :pending_count, :last_interaction_at
|
|
8
|
+
|
|
9
|
+
def initialize(hook:, pending_count:, last_interaction_at:)
|
|
10
|
+
@hook = hook
|
|
11
|
+
@pending_count = pending_count
|
|
12
|
+
@last_interaction_at = last_interaction_at
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Create a HookActivity from an API response hash
|
|
16
|
+
def self.from_hash(hash)
|
|
17
|
+
raise ArgumentError, "Invalid hash: expected Hash, got #{hash.class}" unless hash.is_a?(Hash)
|
|
18
|
+
|
|
19
|
+
new(
|
|
20
|
+
hook: Hook.from_hash(hash['hook']),
|
|
21
|
+
pending_count: hash['pending_count'],
|
|
22
|
+
last_interaction_at: hash['last_interaction_at']
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_s
|
|
27
|
+
"#<Hookd::HookActivity hook=#{hook.id} pending=#{pending_count}>"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/hookd/version.rb
CHANGED
data/lib/hookd.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hookd-client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joshua MARTINELLE
|
|
@@ -36,6 +36,7 @@ files:
|
|
|
36
36
|
- lib/hookd/client.rb
|
|
37
37
|
- lib/hookd/error.rb
|
|
38
38
|
- lib/hookd/hook.rb
|
|
39
|
+
- lib/hookd/hook_activity.rb
|
|
39
40
|
- lib/hookd/interaction.rb
|
|
40
41
|
- lib/hookd/version.rb
|
|
41
42
|
homepage: https://github.com/JoshuaMart/Hookd
|