rerout 0.2.0 → 0.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/CHANGELOG.md +9 -0
- data/README.md +33 -1
- data/lib/rerout/client.rb +5 -0
- data/lib/rerout/create_webhook_input.rb +41 -0
- data/lib/rerout/models.rb +110 -0
- data/lib/rerout/version.rb +1 -1
- data/lib/rerout/webhooks_resource.rb +63 -0
- data/lib/rerout.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6f212d64e3f65e342bc6034aa992c270a2322534dc127e18fa4b7defbd6e0b08
|
|
4
|
+
data.tar.gz: bb2fd2083df69c90e884769fe5aad80b40d11dde7ba7696d96bc04ac73393833
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a86ed8c18689fa52099e11a1fc1bedec3ddd2aa5897ff2d9c694290260892712591bccc61b1aee0129e24e7f2182b5d5dc7657126dfb9960e9cfb6ad3229b79d
|
|
7
|
+
data.tar.gz: 812e84cfb86041e00b6798bd149176818162d7c25f56267dc1be12a7a7c4f5c1c8e816a0744194c0b9ce752619bc090deef52f7742a594a8f6e3ae77f55feb07
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ All notable changes to the `rerout` gem are documented in this file. The
|
|
|
4
4
|
format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.3.0] - 2026-06-03
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Webhook endpoint management via a new `webhooks` namespace — `create`, `list`,
|
|
12
|
+
and `delete` against `/v1/projects/me/webhooks` (API-key auth). The signing
|
|
13
|
+
secret returned by `create` is shown once.
|
|
14
|
+
|
|
7
15
|
## [0.2.0] - 2026-06-02
|
|
8
16
|
|
|
9
17
|
### Added
|
|
@@ -40,5 +48,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
40
48
|
- `Rerout::Error` with stable `code`, `status`, `path`, `timestamp`, `details`
|
|
41
49
|
plus `rate_limited?` and `server_error?` convenience flags.
|
|
42
50
|
|
|
51
|
+
[0.3.0]: https://github.com/ModestNerds-Co/rerout-sdks/releases/tag/ruby/v0.3.0
|
|
43
52
|
[0.2.0]: https://github.com/ModestNerds-Co/rerout-sdks/releases/tag/ruby-v0.2.0
|
|
44
53
|
[0.1.0]: https://github.com/ModestNerds-Co/rerout-sdks/releases/tag/ruby-v0.1.0
|
data/README.md
CHANGED
|
@@ -64,7 +64,7 @@ rerout = Rerout::Client.new(
|
|
|
64
64
|
A blank or missing `api_key` raises `Rerout::Error` with code `missing_api_key`
|
|
65
65
|
before any network call.
|
|
66
66
|
|
|
67
|
-
The client exposes
|
|
67
|
+
The client exposes four namespaces: `links`, `project`, `qr`, and `webhooks`.
|
|
68
68
|
|
|
69
69
|
## Links
|
|
70
70
|
|
|
@@ -147,6 +147,38 @@ File.write('q4.svg', svg)
|
|
|
147
147
|
QR options: `size` (1–32), `margin` (0–16), `ecc` (`L`/`M`/`Q`/`H`), `domain`,
|
|
148
148
|
and `refresh` (`true` is serialized as `1`; a string is sent verbatim).
|
|
149
149
|
|
|
150
|
+
## Webhook management
|
|
151
|
+
|
|
152
|
+
Manage the webhook endpoints that receive event deliveries for the project that
|
|
153
|
+
owns the API key. (This is the `webhooks` namespace — distinct from
|
|
154
|
+
`Rerout::Webhooks`, which verifies inbound signatures below.)
|
|
155
|
+
|
|
156
|
+
```ruby
|
|
157
|
+
# Create — name, url, and events are required.
|
|
158
|
+
created = rerout.webhooks.create(
|
|
159
|
+
Rerout::CreateWebhookInput.new(
|
|
160
|
+
name: 'prod listener',
|
|
161
|
+
url: 'https://hooks.brand.com/rerout',
|
|
162
|
+
events: ['link.created'],
|
|
163
|
+
payload_format: 'json' # optional: "json" (default) or "slack"
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
created.endpoint.id # => "wh_..."
|
|
168
|
+
created.signing_secret # => "whsec_..." — shown ONCE; persist it now.
|
|
169
|
+
|
|
170
|
+
# List endpoints plus the event types the server can deliver.
|
|
171
|
+
result = rerout.webhooks.list
|
|
172
|
+
result.endpoints # => [Rerout::Models::Webhook, ...]
|
|
173
|
+
result.event_types # => ["link.created", ...]
|
|
174
|
+
|
|
175
|
+
# Delete (soft delete) — idempotent.
|
|
176
|
+
rerout.webhooks.delete('wh_...') # => { "deleted" => true }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The `signing_secret` returned by `create` is the value you pass as `secret:` to
|
|
180
|
+
`verify_signature` below — store it securely, as it cannot be retrieved again.
|
|
181
|
+
|
|
150
182
|
## Webhook signature verification
|
|
151
183
|
|
|
152
184
|
Rerout signs every webhook delivery with an `X-Rerout-Signature` header. Verify
|
data/lib/rerout/client.rb
CHANGED
|
@@ -7,12 +7,14 @@ require_relative 'version'
|
|
|
7
7
|
require_relative 'error'
|
|
8
8
|
require_relative 'create_link_input'
|
|
9
9
|
require_relative 'update_link_input'
|
|
10
|
+
require_relative 'create_webhook_input'
|
|
10
11
|
require_relative 'qr_options'
|
|
11
12
|
require_relative 'webhooks'
|
|
12
13
|
require_relative 'models'
|
|
13
14
|
require_relative 'links'
|
|
14
15
|
require_relative 'project'
|
|
15
16
|
require_relative 'qr'
|
|
17
|
+
require_relative 'webhooks_resource'
|
|
16
18
|
|
|
17
19
|
module Rerout
|
|
18
20
|
# Default production API base URL.
|
|
@@ -36,6 +38,8 @@ module Rerout
|
|
|
36
38
|
attr_reader :project
|
|
37
39
|
# @return [Resources::Qr] QR namespace.
|
|
38
40
|
attr_reader :qr
|
|
41
|
+
# @return [Resources::Webhooks] webhook endpoint management namespace.
|
|
42
|
+
attr_reader :webhooks
|
|
39
43
|
|
|
40
44
|
# @param api_key [String] project API key (`rrk_…`). Required.
|
|
41
45
|
# @param base_url [String, nil] override base URL. Defaults to `https://api.rerout.co`.
|
|
@@ -61,6 +65,7 @@ module Rerout
|
|
|
61
65
|
@links = Resources::Links.new(self)
|
|
62
66
|
@project = Resources::Project.new(self)
|
|
63
67
|
@qr = Resources::Qr.new(self)
|
|
68
|
+
@webhooks = Resources::Webhooks.new(self)
|
|
64
69
|
end
|
|
65
70
|
|
|
66
71
|
# Perform a JSON request against the Rerout API.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rerout
|
|
4
|
+
# Request body for `POST /v1/projects/me/webhooks`. `name`, `url`, and
|
|
5
|
+
# `events` are required; `is_active` and `payload_format` are optional and
|
|
6
|
+
# omitted from the payload when not set (server defaults apply).
|
|
7
|
+
class CreateWebhookInput
|
|
8
|
+
attr_reader :name, :url, :events, :is_active, :payload_format
|
|
9
|
+
|
|
10
|
+
# @param name [String] required, human-readable label for the endpoint.
|
|
11
|
+
# @param url [String] required, public https:// URL that receives deliveries.
|
|
12
|
+
# @param events [Array<String>] required, non-empty list of event types
|
|
13
|
+
# to subscribe to (e.g. `link.created`).
|
|
14
|
+
# @param is_active [Boolean, nil] whether the endpoint starts active.
|
|
15
|
+
# Server default: `true`.
|
|
16
|
+
# @param payload_format [String, nil] payload encoding — `"json"` or
|
|
17
|
+
# `"slack"`. Server default: `"json"`.
|
|
18
|
+
def initialize(name:, url:, events:, is_active: nil, payload_format: nil)
|
|
19
|
+
raise ArgumentError, 'name is required' if name.nil? || name.to_s.empty?
|
|
20
|
+
raise ArgumentError, 'url is required' if url.nil? || url.to_s.empty?
|
|
21
|
+
if events.nil? || !events.is_a?(Array) || events.empty?
|
|
22
|
+
raise ArgumentError, 'events is required and must be a non-empty Array'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@name = name
|
|
26
|
+
@url = url
|
|
27
|
+
@events = events
|
|
28
|
+
@is_active = is_active
|
|
29
|
+
@payload_format = payload_format
|
|
30
|
+
freeze
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Serialize for the wire. Optional fields are only included when set.
|
|
34
|
+
def to_h
|
|
35
|
+
hash = { 'name' => name, 'url' => url, 'events' => events }
|
|
36
|
+
hash['is_active'] = is_active unless is_active.nil?
|
|
37
|
+
hash['payload_format'] = payload_format unless payload_format.nil?
|
|
38
|
+
hash
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/rerout/models.rb
CHANGED
|
@@ -301,5 +301,115 @@ module Rerout
|
|
|
301
301
|
[self.class, id, name, slug].hash
|
|
302
302
|
end
|
|
303
303
|
end
|
|
304
|
+
|
|
305
|
+
# A webhook endpoint registered to the project. Mirrors the server-side
|
|
306
|
+
# `WebhookEndpointResponse`.
|
|
307
|
+
class Webhook
|
|
308
|
+
ATTRS = %i[
|
|
309
|
+
id project_id name url events is_active payload_format
|
|
310
|
+
created_at updated_at last_delivery_at last_success_at last_failure_at
|
|
311
|
+
].freeze
|
|
312
|
+
|
|
313
|
+
attr_reader(*ATTRS)
|
|
314
|
+
|
|
315
|
+
def initialize(**attrs)
|
|
316
|
+
ATTRS.each { |k| instance_variable_set(:"@#{k}", attrs[k]) }
|
|
317
|
+
@events = (@events || []).freeze
|
|
318
|
+
freeze
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def self.from_hash(hash)
|
|
322
|
+
new(
|
|
323
|
+
id: hash['id'],
|
|
324
|
+
project_id: hash['project_id'],
|
|
325
|
+
name: hash['name'],
|
|
326
|
+
url: hash['url'],
|
|
327
|
+
events: hash['events'] || [],
|
|
328
|
+
is_active: hash['is_active'],
|
|
329
|
+
payload_format: hash['payload_format'],
|
|
330
|
+
created_at: hash['created_at'],
|
|
331
|
+
updated_at: hash['updated_at'],
|
|
332
|
+
last_delivery_at: hash['last_delivery_at'],
|
|
333
|
+
last_success_at: hash['last_success_at'],
|
|
334
|
+
last_failure_at: hash['last_failure_at']
|
|
335
|
+
)
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def to_h
|
|
339
|
+
ATTRS.to_h { |k| [k, public_send(k)] }
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
def ==(other)
|
|
343
|
+
other.is_a?(Webhook) && other.to_h == to_h
|
|
344
|
+
end
|
|
345
|
+
alias eql? ==
|
|
346
|
+
|
|
347
|
+
def hash
|
|
348
|
+
to_h.hash
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Result of creating a webhook. The `signing_secret` (`whsec_…`) is
|
|
353
|
+
# returned **once** — store it now; it is never shown again.
|
|
354
|
+
class CreatedWebhook
|
|
355
|
+
attr_reader :endpoint, :signing_secret
|
|
356
|
+
|
|
357
|
+
def initialize(endpoint:, signing_secret:)
|
|
358
|
+
@endpoint = endpoint
|
|
359
|
+
@signing_secret = signing_secret
|
|
360
|
+
freeze
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def self.from_hash(hash)
|
|
364
|
+
new(
|
|
365
|
+
endpoint: Webhook.from_hash(hash['endpoint'] || {}),
|
|
366
|
+
signing_secret: hash['signing_secret']
|
|
367
|
+
)
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def to_h
|
|
371
|
+
{ endpoint: endpoint.to_h, signing_secret: signing_secret }
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def ==(other)
|
|
375
|
+
other.is_a?(CreatedWebhook) && other.to_h == to_h
|
|
376
|
+
end
|
|
377
|
+
alias eql? ==
|
|
378
|
+
|
|
379
|
+
def hash
|
|
380
|
+
to_h.hash
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# List of webhook endpoints plus every event type the server can deliver.
|
|
385
|
+
class ListWebhooksResult
|
|
386
|
+
attr_reader :endpoints, :event_types
|
|
387
|
+
|
|
388
|
+
def initialize(endpoints:, event_types:)
|
|
389
|
+
@endpoints = endpoints.freeze
|
|
390
|
+
@event_types = event_types.freeze
|
|
391
|
+
freeze
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def self.from_hash(hash)
|
|
395
|
+
new(
|
|
396
|
+
endpoints: (hash['endpoints'] || []).map { |e| Webhook.from_hash(e) },
|
|
397
|
+
event_types: hash['event_types'] || []
|
|
398
|
+
)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def to_h
|
|
402
|
+
{ endpoints: endpoints.map(&:to_h), event_types: event_types }
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def ==(other)
|
|
406
|
+
other.is_a?(ListWebhooksResult) && other.to_h == to_h
|
|
407
|
+
end
|
|
408
|
+
alias eql? ==
|
|
409
|
+
|
|
410
|
+
def hash
|
|
411
|
+
to_h.hash
|
|
412
|
+
end
|
|
413
|
+
end
|
|
304
414
|
end
|
|
305
415
|
end
|
data/lib/rerout/version.rb
CHANGED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'erb'
|
|
4
|
+
|
|
5
|
+
module Rerout
|
|
6
|
+
module Resources
|
|
7
|
+
# Webhook endpoint management namespace — create, list, delete endpoints
|
|
8
|
+
# for the project that owns the API key.
|
|
9
|
+
#
|
|
10
|
+
# This is distinct from {Rerout::Webhooks}, which verifies *inbound*
|
|
11
|
+
# delivery signatures. Reach it via `client.webhooks`.
|
|
12
|
+
class Webhooks
|
|
13
|
+
# @param client [Rerout::Client]
|
|
14
|
+
def initialize(client)
|
|
15
|
+
@client = client
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Create a webhook endpoint. The returned `signing_secret` (`whsec_…`) is
|
|
19
|
+
# shown once — persist it to verify future deliveries.
|
|
20
|
+
#
|
|
21
|
+
# @param input [Rerout::CreateWebhookInput, Hash] the request body.
|
|
22
|
+
# @return [Rerout::Models::CreatedWebhook]
|
|
23
|
+
def create(input)
|
|
24
|
+
body = coerce_input(input)
|
|
25
|
+
response = @client.request(method: :post, path: '/v1/projects/me/webhooks', body: body)
|
|
26
|
+
Models::CreatedWebhook.from_hash(response)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# List webhook endpoints and the event types the server can deliver.
|
|
30
|
+
#
|
|
31
|
+
# @return [Rerout::Models::ListWebhooksResult]
|
|
32
|
+
def list
|
|
33
|
+
response = @client.request(method: :get, path: '/v1/projects/me/webhooks')
|
|
34
|
+
Models::ListWebhooksResult.from_hash(response)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Soft-delete an endpoint and abandon its pending deliveries. Idempotent.
|
|
38
|
+
#
|
|
39
|
+
# @param endpoint_id [String] the endpoint id (`wh_…`).
|
|
40
|
+
# @return [Hash] `{ "deleted" => true }`
|
|
41
|
+
def delete(endpoint_id)
|
|
42
|
+
@client.request(method: :delete, path: webhook_path(endpoint_id))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def webhook_path(endpoint_id)
|
|
48
|
+
raise ArgumentError, 'endpoint_id is required' if endpoint_id.nil? || endpoint_id.to_s.empty?
|
|
49
|
+
|
|
50
|
+
"/v1/projects/me/webhooks/#{ERB::Util.url_encode(endpoint_id.to_s)}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def coerce_input(input)
|
|
54
|
+
case input
|
|
55
|
+
when CreateWebhookInput then input.to_h
|
|
56
|
+
when Hash then input
|
|
57
|
+
else
|
|
58
|
+
raise ArgumentError, 'input must be a Rerout::CreateWebhookInput or Hash'
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/rerout.rb
CHANGED
|
@@ -5,11 +5,13 @@ require_relative 'rerout/error'
|
|
|
5
5
|
require_relative 'rerout/models'
|
|
6
6
|
require_relative 'rerout/create_link_input'
|
|
7
7
|
require_relative 'rerout/update_link_input'
|
|
8
|
+
require_relative 'rerout/create_webhook_input'
|
|
8
9
|
require_relative 'rerout/qr_options'
|
|
9
10
|
require_relative 'rerout/webhooks'
|
|
10
11
|
require_relative 'rerout/links'
|
|
11
12
|
require_relative 'rerout/project'
|
|
12
13
|
require_relative 'rerout/qr'
|
|
14
|
+
require_relative 'rerout/webhooks_resource'
|
|
13
15
|
require_relative 'rerout/client'
|
|
14
16
|
|
|
15
17
|
# Official Ruby SDK for the Rerout branded-link API.
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rerout
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Codecraft Solutions
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06-
|
|
11
|
+
date: 2026-06-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -110,6 +110,7 @@ files:
|
|
|
110
110
|
- lib/rerout.rb
|
|
111
111
|
- lib/rerout/client.rb
|
|
112
112
|
- lib/rerout/create_link_input.rb
|
|
113
|
+
- lib/rerout/create_webhook_input.rb
|
|
113
114
|
- lib/rerout/error.rb
|
|
114
115
|
- lib/rerout/links.rb
|
|
115
116
|
- lib/rerout/models.rb
|
|
@@ -119,6 +120,7 @@ files:
|
|
|
119
120
|
- lib/rerout/update_link_input.rb
|
|
120
121
|
- lib/rerout/version.rb
|
|
121
122
|
- lib/rerout/webhooks.rb
|
|
123
|
+
- lib/rerout/webhooks_resource.rb
|
|
122
124
|
homepage: https://github.com/ModestNerds-Co/rerout-sdks
|
|
123
125
|
licenses:
|
|
124
126
|
- MIT
|