hooksniff 0.3.0 → 1.1.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.
Files changed (214) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -0
  3. data/Gemfile.lock +56 -0
  4. data/README.md +23 -199
  5. data/Rakefile +2 -0
  6. data/hooksniff.gemspec +48 -0
  7. data/lib/hooksniff/api/authentication.rb +36 -0
  8. data/lib/hooksniff/api/endpoint.rb +102 -0
  9. data/lib/hooksniff/api/event_type.rb +66 -0
  10. data/lib/hooksniff/api/health.rb +16 -0
  11. data/lib/hooksniff/api/integration.rb +42 -0
  12. data/lib/hooksniff/api/message.rb +48 -0
  13. data/lib/hooksniff/api/message_attempt.rb +38 -0
  14. data/lib/hooksniff/api/statistics.rb +37 -0
  15. data/lib/hooksniff/api/stream.rb +45 -0
  16. data/lib/{openapi_client → hooksniff}/api_error.rb +8 -18
  17. data/lib/hooksniff/background_task.rb +21 -0
  18. data/lib/hooksniff/connector.rb +33 -0
  19. data/lib/hooksniff/environment.rb +53 -0
  20. data/lib/hooksniff/errors.rb +129 -0
  21. data/lib/hooksniff/hooksniff.rb +52 -0
  22. data/lib/hooksniff/hooksniff_http_client.rb +128 -0
  23. data/lib/hooksniff/http_error_out.rb +18 -0
  24. data/lib/hooksniff/http_validation_error.rb +18 -0
  25. data/lib/hooksniff/inbound.rb +25 -0
  26. data/lib/hooksniff/internal.rb +7 -0
  27. data/lib/hooksniff/message_poller.rb +32 -0
  28. data/lib/hooksniff/models/endpoint_created_event.rb +50 -0
  29. data/lib/hooksniff/models/endpoint_created_event_data.rb +63 -0
  30. data/lib/hooksniff/models/endpoint_deleted_event.rb +50 -0
  31. data/lib/hooksniff/models/endpoint_deleted_event_data.rb +63 -0
  32. data/lib/hooksniff/models/endpoint_disabled_event.rb +53 -0
  33. data/lib/hooksniff/models/endpoint_disabled_event_data.rb +69 -0
  34. data/lib/hooksniff/models/endpoint_enabled_event.rb +50 -0
  35. data/lib/hooksniff/models/endpoint_enabled_event_data.rb +63 -0
  36. data/lib/hooksniff/models/endpoint_headers_in.rb +46 -0
  37. data/lib/hooksniff/models/endpoint_headers_out.rb +52 -0
  38. data/lib/hooksniff/models/endpoint_headers_patch_in.rb +53 -0
  39. data/lib/hooksniff/models/endpoint_in.rb +102 -0
  40. data/lib/hooksniff/models/endpoint_out.rb +104 -0
  41. data/lib/hooksniff/models/endpoint_patch.rb +97 -0
  42. data/lib/hooksniff/models/endpoint_secret_out.rb +50 -0
  43. data/lib/hooksniff/models/endpoint_secret_rotate_in.rb +53 -0
  44. data/lib/hooksniff/models/endpoint_update.rb +90 -0
  45. data/lib/hooksniff/models/endpoint_updated_event.rb +50 -0
  46. data/lib/hooksniff/models/endpoint_updated_event_data.rb +63 -0
  47. data/lib/hooksniff/models/event_in.rb +50 -0
  48. data/lib/hooksniff/models/event_out.rb +53 -0
  49. data/lib/hooksniff/models/event_type_in.rb +80 -0
  50. data/lib/hooksniff/models/event_type_out.rb +87 -0
  51. data/lib/hooksniff/models/event_type_patch.rb +66 -0
  52. data/lib/hooksniff/models/event_type_update.rb +67 -0
  53. data/lib/hooksniff/models/list_response_endpoint_out.rb +58 -0
  54. data/lib/hooksniff/models/list_response_event_type_out.rb +58 -0
  55. data/lib/hooksniff/models/list_response_message_attempt_out.rb +58 -0
  56. data/lib/hooksniff/models/list_response_message_out.rb +58 -0
  57. data/lib/hooksniff/models/message_attempt_exhausted_event.rb +53 -0
  58. data/lib/hooksniff/models/message_attempt_exhausted_event_data.rb +70 -0
  59. data/lib/hooksniff/models/message_attempt_failed_data.rb +56 -0
  60. data/lib/hooksniff/models/message_attempt_failing_event.rb +54 -0
  61. data/lib/hooksniff/models/message_attempt_failing_event_data.rb +70 -0
  62. data/lib/hooksniff/models/message_attempt_log.rb +112 -0
  63. data/lib/hooksniff/models/message_attempt_log_event.rb +53 -0
  64. data/lib/hooksniff/models/message_attempt_out.rb +96 -0
  65. data/lib/hooksniff/models/message_attempt_trigger_type.rb +33 -0
  66. data/lib/hooksniff/models/message_endpoint_out.rb +112 -0
  67. data/lib/hooksniff/models/message_in.rb +99 -0
  68. data/lib/hooksniff/models/message_out.rb +71 -0
  69. data/lib/hooksniff/models/message_status.rb +39 -0
  70. data/lib/hooksniff/models/message_status_text.rb +32 -0
  71. data/lib/hooksniff/models/ordering.rb +30 -0
  72. data/lib/hooksniff/models/status_code_class.rb +41 -0
  73. data/lib/hooksniff/operational_webhook.rb +12 -0
  74. data/lib/hooksniff/util.rb +69 -0
  75. data/lib/hooksniff/validation_error.rb +28 -0
  76. data/lib/hooksniff/version.rb +5 -0
  77. data/lib/hooksniff/webhook.rb +84 -0
  78. data/lib/hooksniff.rb +78 -0
  79. data/test/test_hooksniff.rb +86 -0
  80. metadata +131 -159
  81. data/lib/openapi_client/api/admin_api.rb +0 -452
  82. data/lib/openapi_client/api/alerts_api.rb +0 -322
  83. data/lib/openapi_client/api/analytics_api.rb +0 -208
  84. data/lib/openapi_client/api/api_keys_api.rb +0 -252
  85. data/lib/openapi_client/api/audit_log_api.rb +0 -140
  86. data/lib/openapi_client/api/auth_api.rb +0 -1080
  87. data/lib/openapi_client/api/billing_api.rb +0 -500
  88. data/lib/openapi_client/api/contact_api.rb +0 -88
  89. data/lib/openapi_client/api/custom_domains_api.rb +0 -253
  90. data/lib/openapi_client/api/customer_portal_api.rb +0 -700
  91. data/lib/openapi_client/api/delivery_details_api.rb +0 -146
  92. data/lib/openapi_client/api/devices_api.rb +0 -202
  93. data/lib/openapi_client/api/embed_api.rb +0 -128
  94. data/lib/openapi_client/api/endpoints_api.rb +0 -468
  95. data/lib/openapi_client/api/events_api.rb +0 -75
  96. data/lib/openapi_client/api/health_api.rb +0 -193
  97. data/lib/openapi_client/api/inbound_api.rb +0 -170
  98. data/lib/openapi_client/api/notifications_api.rb +0 -309
  99. data/lib/openapi_client/api/o_auth_api.rb +0 -181
  100. data/lib/openapi_client/api/outbound_ips_api.rb +0 -77
  101. data/lib/openapi_client/api/playground_api.rb +0 -143
  102. data/lib/openapi_client/api/rate_limits_api.rb +0 -252
  103. data/lib/openapi_client/api/routing_api.rb +0 -393
  104. data/lib/openapi_client/api/schemas_api.rb +0 -268
  105. data/lib/openapi_client/api/search_api.rb +0 -96
  106. data/lib/openapi_client/api/simulator_api.rb +0 -82
  107. data/lib/openapi_client/api/sso_api.rb +0 -241
  108. data/lib/openapi_client/api/stats_api.rb +0 -77
  109. data/lib/openapi_client/api/stream_api.rb +0 -88
  110. data/lib/openapi_client/api/teams_api.rb +0 -476
  111. data/lib/openapi_client/api/templates_api.rb +0 -213
  112. data/lib/openapi_client/api/transforms_api.rb +0 -368
  113. data/lib/openapi_client/api/webhooks_api.rb +0 -534
  114. data/lib/openapi_client/api_client.rb +0 -397
  115. data/lib/openapi_client/api_model_base.rb +0 -88
  116. data/lib/openapi_client/configuration.rb +0 -312
  117. data/lib/openapi_client/models/admin_revenue_get200_response_inner.rb +0 -165
  118. data/lib/openapi_client/models/admin_sdk_update_post_request.rb +0 -156
  119. data/lib/openapi_client/models/admin_users_id_plan_put_request.rb +0 -181
  120. data/lib/openapi_client/models/admin_users_id_status_put_request.rb +0 -147
  121. data/lib/openapi_client/models/alert_rule.rb +0 -237
  122. data/lib/openapi_client/models/api_key_info.rb +0 -185
  123. data/lib/openapi_client/models/apply_template_request.rb +0 -173
  124. data/lib/openapi_client/models/apply_template_response.rb +0 -156
  125. data/lib/openapi_client/models/auth2fa_enable_post200_response.rb +0 -156
  126. data/lib/openapi_client/models/auth_login_post200_response.rb +0 -104
  127. data/lib/openapi_client/models/auth_response.rb +0 -167
  128. data/lib/openapi_client/models/batch_replay_request.rb +0 -166
  129. data/lib/openapi_client/models/batch_response.rb +0 -160
  130. data/lib/openapi_client/models/batch_response_errors_inner.rb +0 -156
  131. data/lib/openapi_client/models/batch_webhook_request.rb +0 -166
  132. data/lib/openapi_client/models/billing_portal_post200_response.rb +0 -147
  133. data/lib/openapi_client/models/change_password_request.rb +0 -199
  134. data/lib/openapi_client/models/change_role_request.rb +0 -188
  135. data/lib/openapi_client/models/confirm2fa_request.rb +0 -182
  136. data/lib/openapi_client/models/contact_request.rb +0 -242
  137. data/lib/openapi_client/models/contact_response.rb +0 -156
  138. data/lib/openapi_client/models/create_alert_request.rb +0 -277
  139. data/lib/openapi_client/models/create_api_key_response.rb +0 -175
  140. data/lib/openapi_client/models/create_endpoint_request.rb +0 -288
  141. data/lib/openapi_client/models/create_team_request.rb +0 -164
  142. data/lib/openapi_client/models/create_transform_rule_request.rb +0 -216
  143. data/lib/openapi_client/models/create_webhook_request.rb +0 -201
  144. data/lib/openapi_client/models/custom_domains_post_request.rb +0 -147
  145. data/lib/openapi_client/models/customer_response.rb +0 -256
  146. data/lib/openapi_client/models/delivery.rb +0 -246
  147. data/lib/openapi_client/models/delivery_attempt.rb +0 -205
  148. data/lib/openapi_client/models/delivery_list_response.rb +0 -176
  149. data/lib/openapi_client/models/delivery_trend_response.rb +0 -158
  150. data/lib/openapi_client/models/delivery_trend_response_buckets_inner.rb +0 -174
  151. data/lib/openapi_client/models/device_token_response.rb +0 -174
  152. data/lib/openapi_client/models/disable2fa_request.rb +0 -164
  153. data/lib/openapi_client/models/enable2fa_request.rb +0 -164
  154. data/lib/openapi_client/models/endpoint.rb +0 -321
  155. data/lib/openapi_client/models/endpoint_health.rb +0 -183
  156. data/lib/openapi_client/models/endpoints_endpoint_id_transforms_test_post_request.rb +0 -156
  157. data/lib/openapi_client/models/endpoints_id_rotate_secret_post200_response.rb +0 -156
  158. data/lib/openapi_client/models/error.rb +0 -165
  159. data/lib/openapi_client/models/forgot_password_request.rb +0 -164
  160. data/lib/openapi_client/models/invite_request.rb +0 -207
  161. data/lib/openapi_client/models/invoice_response.rb +0 -183
  162. data/lib/openapi_client/models/latency_trend_response.rb +0 -167
  163. data/lib/openapi_client/models/latency_trend_response_buckets_inner.rb +0 -165
  164. data/lib/openapi_client/models/login_request.rb +0 -190
  165. data/lib/openapi_client/models/notification.rb +0 -193
  166. data/lib/openapi_client/models/notification_list_response.rb +0 -167
  167. data/lib/openapi_client/models/notification_preferences.rb +0 -201
  168. data/lib/openapi_client/models/notifications_unread_count_get200_response.rb +0 -147
  169. data/lib/openapi_client/models/outbound_ips_response.rb +0 -158
  170. data/lib/openapi_client/models/paginated_users.rb +0 -176
  171. data/lib/openapi_client/models/playground_get200_response.rb +0 -160
  172. data/lib/openapi_client/models/portal_notifications_put200_response.rb +0 -156
  173. data/lib/openapi_client/models/portal_profile.rb +0 -184
  174. data/lib/openapi_client/models/refresh_token_request.rb +0 -164
  175. data/lib/openapi_client/models/register_device_request.rb +0 -208
  176. data/lib/openapi_client/models/register_request.rb +0 -201
  177. data/lib/openapi_client/models/register_schema_request.rb +0 -191
  178. data/lib/openapi_client/models/resend_verification_request.rb +0 -164
  179. data/lib/openapi_client/models/reset_password_request.rb +0 -199
  180. data/lib/openapi_client/models/retry_policy.rb +0 -216
  181. data/lib/openapi_client/models/routing_info.rb +0 -193
  182. data/lib/openapi_client/models/search_result.rb +0 -158
  183. data/lib/openapi_client/models/simulator_post_request.rb +0 -165
  184. data/lib/openapi_client/models/sso_config_post_request.rb +0 -190
  185. data/lib/openapi_client/models/stats_response.rb +0 -210
  186. data/lib/openapi_client/models/stream_params.rb +0 -201
  187. data/lib/openapi_client/models/subscription_response.rb +0 -201
  188. data/lib/openapi_client/models/success_rate_response.rb +0 -183
  189. data/lib/openapi_client/models/system_stats.rb +0 -185
  190. data/lib/openapi_client/models/system_stats_plan_breakdown_inner.rb +0 -156
  191. data/lib/openapi_client/models/system_status.rb +0 -210
  192. data/lib/openapi_client/models/system_status_components_inner.rb +0 -184
  193. data/lib/openapi_client/models/team.rb +0 -165
  194. data/lib/openapi_client/models/team_detail_response.rb +0 -169
  195. data/lib/openapi_client/models/team_invite.rb +0 -174
  196. data/lib/openapi_client/models/team_member.rb +0 -193
  197. data/lib/openapi_client/models/test_webhook_request.rb +0 -199
  198. data/lib/openapi_client/models/test_webhook_response.rb +0 -174
  199. data/lib/openapi_client/models/transform_rule.rb +0 -201
  200. data/lib/openapi_client/models/two_factor_required_response.rb +0 -165
  201. data/lib/openapi_client/models/update_endpoint_request.rb +0 -278
  202. data/lib/openapi_client/models/update_notification_preferences.rb +0 -195
  203. data/lib/openapi_client/models/update_profile_request.rb +0 -190
  204. data/lib/openapi_client/models/update_routing_request.rb +0 -190
  205. data/lib/openapi_client/models/upgrade_request.rb +0 -209
  206. data/lib/openapi_client/models/upgrade_response.rb +0 -166
  207. data/lib/openapi_client/models/usage_response.rb +0 -201
  208. data/lib/openapi_client/models/user_summary.rb +0 -193
  209. data/lib/openapi_client/models/validate_event_request.rb +0 -164
  210. data/lib/openapi_client/models/verify2fa_request.rb +0 -208
  211. data/lib/openapi_client/models/verify_email_request.rb +0 -164
  212. data/lib/openapi_client/models/webhook_template.rb +0 -183
  213. data/lib/openapi_client/version.rb +0 -15
  214. data/lib/openapi_client.rb +0 -169
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97532c0ab6de4c3b1427b22a571a2637cb1d8c666b5d90c96a303ee43b100e8b
4
- data.tar.gz: 40cf971ef0f0b0641621af647b3791a87821c894545eac6366ea4c05df888c84
3
+ metadata.gz: d9eed443c71c5bfe95ea6f2aa6173480e01b1b2528d064e52071e07dff360144
4
+ data.tar.gz: c3c58215fdbe0d8e3110dd1359b5662963999ea887ed3aa6d2b2bfabb4bc79a5
5
5
  SHA512:
6
- metadata.gz: 0fc3db5d623e798a34e9983930a97aa8a05dd319cf48004d34dc4fa5dfeee54d6c40a0350469950ba985cd77ee7e5d4ca4b11757d051540036db213e96920610
7
- data.tar.gz: ca1157a3ec1a9384ad2a2fae4fe185f0b761f4946cbcbc0eef435ba9d410f9b3f2724046edaa6980970e583c72b7640d71e77c42f2f2f6ab72bc0e235dcc5b8c
6
+ metadata.gz: 2491e4a7ce09b46eedd9e40696c3f35d1a587d2be02a79c8dbfdc0879135d1ef0e5936ce58a1e64a7c5e82044358a63a61926d5dd5a6d3e91087cac9705cabdd
7
+ data.tar.gz: d6e137768bbf254de889673110bfb8826029b5b323daef6453d1b01921e2bfe81a02869b008d4c86aad63c56eae07c71a205d0a9f471e96604f8c001d6abfe21
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'base64'
4
+ gem 'logger'
5
+ gem 'ostruct'
6
+
7
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,56 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ svix (1.93.0)
5
+ base64 (~> 0.3.0)
6
+ logger (~> 1.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.9.0)
12
+ public_suffix (>= 2.0.2, < 8.0)
13
+ base64 (0.3.0)
14
+ bigdecimal (4.1.1)
15
+ crack (1.0.1)
16
+ bigdecimal
17
+ rexml
18
+ diff-lcs (1.6.2)
19
+ hashdiff (1.2.1)
20
+ logger (1.7.0)
21
+ ostruct (0.6.3)
22
+ public_suffix (7.0.5)
23
+ rake (13.4.1)
24
+ rexml (3.4.4)
25
+ rspec (3.13.2)
26
+ rspec-core (~> 3.13.0)
27
+ rspec-expectations (~> 3.13.0)
28
+ rspec-mocks (~> 3.13.0)
29
+ rspec-core (3.13.6)
30
+ rspec-support (~> 3.13.0)
31
+ rspec-expectations (3.13.5)
32
+ diff-lcs (>= 1.2.0, < 2.0)
33
+ rspec-support (~> 3.13.0)
34
+ rspec-mocks (3.13.8)
35
+ diff-lcs (>= 1.2.0, < 2.0)
36
+ rspec-support (~> 3.13.0)
37
+ rspec-support (3.13.7)
38
+ webmock (3.26.2)
39
+ addressable (>= 2.8.0)
40
+ crack (>= 0.3.2)
41
+ hashdiff (>= 0.4.0, < 2.0.0)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ base64
48
+ logger
49
+ ostruct
50
+ rake (~> 13.0)
51
+ rspec (~> 3.2)
52
+ svix!
53
+ webmock (~> 3.25)
54
+
55
+ BUNDLED WITH
56
+ 2.6.5
data/README.md CHANGED
@@ -1,20 +1,13 @@
1
1
  # HookSniff Ruby SDK
2
2
 
3
- [![Gem](https://img.shields.io/gem/v/hooksniff.svg)](https://rubygems.org/gems/hooksniff)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
3
+ <p align="center">
4
+ <a href="https://github.com/servetarslan02/HookSniff"><img src="https://img.shields.io/github/license/servetarslan02/HookSniff" alt="License"></a>
5
+ </p>
5
6
 
6
- Official Ruby client for the [HookSniff](https://hooksniff.vercel.app) webhook delivery service.
7
+ Ruby SDK for the [HookSniff](https://hooksniff.com) webhook delivery platform.
7
8
 
8
9
  ## Installation
9
10
 
10
- Add to your Gemfile:
11
-
12
- ```ruby
13
- gem "hooksniff"
14
- ```
15
-
16
- Or install directly:
17
-
18
11
  ```bash
19
12
  gem install hooksniff
20
13
  ```
@@ -24,200 +17,31 @@ gem install hooksniff
24
17
  ```ruby
25
18
  require "hooksniff"
26
19
 
27
- # Initialize client
28
- client = HookSniff::Client.new(api_key: "hr_live_your_api_key_here")
29
-
30
- # Create a webhook endpoint
31
- endpoint = client.endpoints.create(
32
- url: "https://myapp.com/webhook",
33
- description: "Order notifications"
34
- )
35
- puts "Endpoint created: #{endpoint[:id]}"
36
-
37
- # Send a webhook
38
- delivery = client.webhooks.send(
39
- endpoint_id: endpoint[:id],
40
- event: "order.created",
41
- data: { order_id: "12345", amount: 99.99 }
42
- )
43
- puts "Delivery queued: #{delivery[:id]}, status: #{delivery[:status]}"
44
-
45
- # Check delivery status
46
- status = client.webhooks.get(delivery[:id])
47
- puts "Status: #{status[:status]}, attempts: #{status[:attempt_count]}"
48
-
49
- # List deliveries
50
- deliveries = client.webhooks.list(status: "failed", page: 1)
51
- deliveries[:deliveries].each do |d|
52
- puts " #{d[:id]}: #{d[:status]}"
53
- end
54
-
55
- # Replay a failed delivery
56
- replayed = client.webhooks.replay(delivery[:id])
57
- puts "Replay queued: #{replayed[:id]}"
20
+ client = HookSniff::Client.new("hs_xxx")
21
+ endpoints = client.endpoint.list
22
+ puts endpoints
58
23
  ```
59
24
 
60
- ## Batch Webhooks
61
-
62
- Send multiple webhooks in a single request (max 100):
25
+ ## Webhook Verification
63
26
 
64
27
  ```ruby
65
- results = client.webhooks.batch([
66
- { endpoint_id: "ep_1", event: "order.created", data: { order_id: "12345" } },
67
- { endpoint_id: "ep_2", event: "payment.completed", data: { payment_id: "pay_67890" } }
68
- ])
69
-
70
- puts "Delivered: #{results[:deliveries].length}"
71
- puts "Errors: #{results[:errors].length}"
72
- results[:errors].each do |err|
73
- puts " Item #{err['index']}: #{err['error']}"
74
- end
28
+ wh = HookSniff::Webhook.new("whsec_xxx")
29
+ payload = wh.verify(body, headers)
75
30
  ```
76
31
 
77
- ## Retry Policy
78
-
79
- Configure custom retry behavior when creating endpoints:
80
-
81
- ```ruby
82
- endpoint = client.endpoints.create(
83
- url: "https://myapp.com/webhook",
84
- description: "Critical notifications",
85
- retry_policy: {
86
- max_attempts: 5,
87
- backoff: "exponential",
88
- initial_delay_secs: 10,
89
- max_delay_secs: 3600
90
- }
91
- )
92
- ```
93
-
94
- ## Delivery Attempts
95
-
96
- Inspect individual delivery attempts:
97
-
98
- ```ruby
99
- attempts = client.webhooks.attempts(delivery[:id])
100
- attempts.each do |attempt|
101
- puts " Attempt #{attempt[:attempt_number]}: status=#{attempt[:status_code]}, " \
102
- "duration=#{attempt[:duration_ms]}ms"
103
- puts " Error: #{attempt[:error_message]}" if attempt[:error_message]
104
- end
105
- ```
106
-
107
- ## Export Logs
108
-
109
- Export webhook logs as JSON or CSV:
110
-
111
- ```ruby
112
- # JSON export
113
- logs = client.webhooks.export(format: "json", status: "failed")
114
-
115
- # CSV export
116
- csv_data = client.webhooks.export(format: "csv", date_from: "2024-01-01")
117
- File.write("webhooks.csv", csv_data)
118
- ```
119
-
120
- ## Signature Verification
121
-
122
- Verify incoming webhook signatures in your handler:
123
-
124
- ```ruby
125
- require "hooksniff"
126
- require "sinatra"
127
-
128
- post "/webhook" do
129
- payload = request.body.read
130
- signature = request.env["HTTP_X_HOOKSNIFF_SIGNATURE"]
131
- secret = "whsec_your_endpoint_signing_secret"
132
-
133
- unless HookSniff.verify_signature(payload, signature, secret)
134
- halt 401, { error: "Invalid signature" }.to_json
135
- end
136
-
137
- data = JSON.parse(payload)
138
- puts "Received event: #{data['event']}"
139
- content_type :json
140
- { received: true }.to_json
141
- end
142
- ```
143
-
144
- ### Standard Webhooks Verification
145
-
146
- For Standard Webhooks compatible verification:
147
-
148
- ```ruby
149
- result = HookSniff.verify_webhook(
150
- payload: request.body.read,
151
- msg_id: request.env["HTTP_WEBHOOK_ID"],
152
- timestamp: request.env["HTTP_WEBHOOK_TIMESTAMP"],
153
- signature_header: request.env["HTTP_WEBHOOK_SIGNATURE"],
154
- secret: "whsec_..."
155
- )
156
-
157
- unless result[:valid]
158
- halt 401, { error: result[:error] }.to_json
159
- end
160
-
161
- puts "Event: #{result[:payload]['event']}"
162
- ```
163
-
164
- ## Error Handling
165
-
166
- ```ruby
167
- begin
168
- delivery = client.webhooks.send(
169
- endpoint_id: "nonexistent",
170
- event: "test.event",
171
- data: { test: true }
172
- )
173
- rescue HookSniff::AuthenticationError => e
174
- puts "Invalid API key"
175
- rescue HookSniff::NotFoundError => e
176
- puts "Endpoint not found"
177
- rescue HookSniff::RateLimitError => e
178
- puts "Rate limit exceeded - try again later"
179
- rescue HookSniff::ValidationError => e
180
- puts "Invalid request: #{e.message}"
181
- rescue HookSniff::PayloadTooLargeError => e
182
- puts "Payload exceeds maximum size"
183
- end
184
- ```
185
-
186
- ## API Reference
187
-
188
- ### `HookSniff::Client.new(api_key:, base_url: nil, timeout: nil)`
189
-
190
- | Option | Type | Default | Description |
191
- |--------|------|---------|-------------|
192
- | `api_key` | `String` | required | Your HookSniff API key |
193
- | `base_url` | `String` | `https://hooksniff-api-1046140057667.europe-west1.run.app/v1` | API base URL |
194
- | `timeout` | `Integer` | `30` | Request timeout in seconds |
195
-
196
- ### `client.endpoints`
197
-
198
- - `.create(url:, description: nil, retry_policy: nil)` → `Hash`
199
- - `.get(endpoint_id)` → `Hash`
200
- - `.list` → `Array<Hash>`
201
- - `.delete(endpoint_id)` → `Boolean`
202
-
203
- ### `client.webhooks`
204
-
205
- - `.send(endpoint_id:, data:, event: nil)` → `Hash`
206
- - `.get(delivery_id)` → `Hash`
207
- - `.list(status: nil, page: 1, per_page: 20)` → `Hash`
208
- - `.replay(delivery_id)` → `Hash`
209
- - `.batch(webhooks)` → `Hash`
210
- - `.attempts(delivery_id)` → `Array<Hash>`
211
- - `.export(format: nil, status: nil, date_from: nil, date_to: nil)` → `Array<Hash> | String`
212
-
213
- ### `HookSniff.verify_signature(payload, signature, secret)` → `Boolean`
214
-
215
- Verify a webhook signature using HMAC-SHA256.
216
-
217
- ### `HookSniff.verify_webhook(payload:, msg_id:, timestamp:, signature_header:, secret:, tolerance_secs: 300)` → `Hash`
32
+ ## Resources
218
33
 
219
- Verify a webhook using Standard Webheaders headers.
34
+ | Resource | Methods |
35
+ |----------|---------|
36
+ | Endpoint | list, create, get, update, delete |
37
+ | Message | create, list, get |
38
+ | MessageAttempt | list, listByMsg, get, resend |
39
+ | Authentication | dashboardAccess |
40
+ | EventType | list |
41
+ | Statistics | aggregate |
220
42
 
221
- ## License
43
+ ## Links
222
44
 
223
- MIT
45
+ - [Documentation](https://docs.hooksniff.com)
46
+ - [API Reference](https://api.hooksniff.com)
47
+ - [GitHub](https://github.com/servetarslan02/HookSniff)
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/hooksniff.gemspec ADDED
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "hooksniff/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "hooksniff"
9
+ spec.version = HookSniff::VERSION
10
+ spec.authors = ["HookSniff"]
11
+ spec.email = ["support@hooksniff.vercel.app"]
12
+ spec.license = "MIT"
13
+
14
+ spec.summary = "HookSniff webhooks API client and webhook verification library"
15
+ spec.description = "HookSniff makes webhooks easy and reliable. " \
16
+ "Learn more at https://hooksniff.vercel.app"
17
+ spec.homepage = "https://hooksniff.vercel.app"
18
+
19
+ spec.required_ruby_version = ">= 3.2"
20
+
21
+ if spec.respond_to?(:metadata)
22
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
23
+ spec.metadata["homepage_uri"] = spec.homepage
24
+ spec.metadata["source_code_uri"] = "https://github.com/servetarslan02/HookSniff"
25
+ else
26
+ raise "RubyGems 2.0 or newer is required to protect against " \
27
+ "public gem pushes."
28
+ end
29
+
30
+ ignored = Regexp.union(
31
+ /\Aspec/,
32
+ /\Apkg/,
33
+ /\A.gitignore/,
34
+ /.gem\z/
35
+ )
36
+ spec.files = Dir['**/*'].reject {|f| !File.file?(f) || ignored.match(f) }
37
+
38
+ spec.bindir = "exe"
39
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
40
+ spec.require_paths = ["lib"]
41
+
42
+ spec.add_dependency "base64", "~> 0.3.0"
43
+ spec.add_dependency "logger", "~> 1.0"
44
+
45
+ spec.add_development_dependency "rake", "~> 13.0"
46
+ spec.add_development_dependency "rspec", "~> 3.2"
47
+ spec.add_development_dependency "webmock", "~> 3.25"
48
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+
5
+ module HookSniff
6
+ class Authentication
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def app_portal_access(app_id, app_portal_access_in, options = {})
12
+ options = options.transform_keys(&:to_s)
13
+ res = @client.execute_request(
14
+ "POST",
15
+ "/v1/auth/portal-access/#{app_id}",
16
+ headers: {
17
+ "idempotency-key" => options["idempotency-key"]
18
+ },
19
+ body: app_portal_access_in
20
+ )
21
+ DashboardAccessOut.deserialize(res)
22
+ end
23
+
24
+ def logout(options = {})
25
+ options = options.transform_keys(&:to_s)
26
+ @client.execute_request(
27
+ "POST",
28
+ "/v1/auth/logout",
29
+ headers: {
30
+ "idempotency-key" => options["idempotency-key"]
31
+ }
32
+ )
33
+ nil
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+
5
+ module HookSniff
6
+ class Endpoint
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def list(options = {})
12
+ options = options.transform_keys(&:to_s)
13
+ res = @client.execute_request(
14
+ "GET",
15
+ "/v1/endpoints",
16
+ query_params: {
17
+ "limit" => options["limit"],
18
+ "offset" => options["offset"]
19
+ }
20
+ )
21
+ ListResponseEndpointOut.deserialize(res)
22
+ end
23
+
24
+ def create(endpoint_in, options = {})
25
+ options = options.transform_keys(&:to_s)
26
+ res = @client.execute_request(
27
+ "POST",
28
+ "/v1/endpoints",
29
+ headers: {
30
+ "idempotency-key" => options["idempotency-key"]
31
+ },
32
+ body: endpoint_in
33
+ )
34
+ EndpointOut.deserialize(res)
35
+ end
36
+
37
+ def get(endpoint_id)
38
+ res = @client.execute_request("GET", "/v1/endpoints/#{endpoint_id}")
39
+ EndpointOut.deserialize(res)
40
+ end
41
+
42
+ def update(endpoint_id, endpoint_update)
43
+ res = @client.execute_request(
44
+ "PUT",
45
+ "/v1/endpoints/#{endpoint_id}",
46
+ body: endpoint_update
47
+ )
48
+ EndpointOut.deserialize(res)
49
+ end
50
+
51
+ def patch(endpoint_id, endpoint_patch)
52
+ res = @client.execute_request(
53
+ "PATCH",
54
+ "/v1/endpoints/#{endpoint_id}",
55
+ body: endpoint_patch
56
+ )
57
+ EndpointOut.deserialize(res)
58
+ end
59
+
60
+ def delete(endpoint_id)
61
+ @client.execute_request("DELETE", "/v1/endpoints/#{endpoint_id}")
62
+ nil
63
+ end
64
+
65
+ def rotate_secret(endpoint_id, endpoint_secret_rotate_in)
66
+ res = @client.execute_request(
67
+ "POST",
68
+ "/v1/endpoints/#{endpoint_id}/rotate-secret",
69
+ body: endpoint_secret_rotate_in
70
+ )
71
+ EndpointSecretOut.deserialize(res)
72
+ end
73
+
74
+ def get_headers(endpoint_id)
75
+ res = @client.execute_request("GET", "/v1/endpoints/#{endpoint_id}/headers")
76
+ EndpointHeadersOut.deserialize(res)
77
+ end
78
+
79
+ def update_headers(endpoint_id, endpoint_headers_in)
80
+ @client.execute_request(
81
+ "PUT",
82
+ "/v1/endpoints/#{endpoint_id}/headers",
83
+ body: endpoint_headers_in
84
+ )
85
+ nil
86
+ end
87
+
88
+ def patch_headers(endpoint_id, endpoint_headers_patch_in)
89
+ @client.execute_request(
90
+ "PATCH",
91
+ "/v1/endpoints/#{endpoint_id}/headers",
92
+ body: endpoint_headers_patch_in
93
+ )
94
+ nil
95
+ end
96
+
97
+ def get_secret(endpoint_id)
98
+ res = @client.execute_request("GET", "/v1/endpoints/#{endpoint_id}/secret")
99
+ EndpointSecretOut.deserialize(res)
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+
5
+ module HookSniff
6
+ class EventType
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def list(options = {})
12
+ options = options.transform_keys(&:to_s)
13
+ res = @client.execute_request(
14
+ "GET",
15
+ "/v1/event-types",
16
+ query_params: {
17
+ "limit" => options["limit"],
18
+ "offset" => options["offset"],
19
+ "include_archived" => options["include_archived"]
20
+ }
21
+ )
22
+ ListResponseEventTypeOut.deserialize(res)
23
+ end
24
+
25
+ def create(event_type_in, options = {})
26
+ options = options.transform_keys(&:to_s)
27
+ res = @client.execute_request(
28
+ "POST",
29
+ "/v1/event-types",
30
+ headers: {
31
+ "idempotency-key" => options["idempotency-key"]
32
+ },
33
+ body: event_type_in
34
+ )
35
+ EventTypeOut.deserialize(res)
36
+ end
37
+
38
+ def get(event_type_name)
39
+ res = @client.execute_request("GET", "/v1/event-types/#{event_type_name}")
40
+ EventTypeOut.deserialize(res)
41
+ end
42
+
43
+ def update(event_type_name, event_type_update)
44
+ res = @client.execute_request(
45
+ "PUT",
46
+ "/v1/event-types/#{event_type_name}",
47
+ body: event_type_update
48
+ )
49
+ EventTypeOut.deserialize(res)
50
+ end
51
+
52
+ def patch(event_type_name, event_type_patch)
53
+ res = @client.execute_request(
54
+ "PATCH",
55
+ "/v1/event-types/#{event_type_name}",
56
+ body: event_type_patch
57
+ )
58
+ EventTypeOut.deserialize(res)
59
+ end
60
+
61
+ def delete(event_type_name)
62
+ @client.execute_request("DELETE", "/v1/event-types/#{event_type_name}")
63
+ nil
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+
5
+ module HookSniff
6
+ class Health
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def get
12
+ res = @client.execute_request("GET", "/v1/health")
13
+ res
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HookSniff
4
+ class IntegrationApi
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def list
10
+ @client.request(:get, "/api/v1/integrations")
11
+ end
12
+
13
+ def get(id)
14
+ @client.request(:get, "/api/v1/integrations/#{id}")
15
+ end
16
+
17
+ create_attrs = %i[name description connector_config_id endpoint_id event_filter transform_id retry_policy metadata enabled]
18
+ def create(attrs)
19
+ @client.request(:post, "/api/v1/integrations", attrs)
20
+ end
21
+
22
+ def update(id, attrs)
23
+ @client.request(:put, "/api/v1/integrations/#{id}", attrs)
24
+ end
25
+
26
+ def delete(id)
27
+ @client.request(:delete, "/api/v1/integrations/#{id}")
28
+ end
29
+
30
+ def test(id)
31
+ @client.request(:post, "/api/v1/integrations/#{id}/test")
32
+ end
33
+
34
+ def list_events(id, params = {})
35
+ @client.request(:get, "/api/v1/integrations/#{id}/events", params)
36
+ end
37
+
38
+ def get_stats(id)
39
+ @client.request(:get, "/api/v1/integrations/#{id}/stats")
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+
5
+ module HookSniff
6
+ class Message
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def create(message_in, options = {})
12
+ options = options.transform_keys(&:to_s)
13
+ res = @client.execute_request(
14
+ "POST",
15
+ "/v1/webhooks",
16
+ headers: {
17
+ "idempotency-key" => options["idempotency-key"]
18
+ },
19
+ body: message_in
20
+ )
21
+ MessageOut.deserialize(res)
22
+ end
23
+
24
+ def list(options = {})
25
+ options = options.transform_keys(&:to_s)
26
+ res = @client.execute_request(
27
+ "GET",
28
+ "/v1/webhooks",
29
+ query_params: {
30
+ "limit" => options["limit"],
31
+ "offset" => options["offset"],
32
+ "status" => options["status"]
33
+ }
34
+ )
35
+ ListResponseMessageOut.deserialize(res)
36
+ end
37
+
38
+ def get(message_id)
39
+ res = @client.execute_request("GET", "/v1/webhooks/#{message_id}")
40
+ MessageOut.deserialize(res)
41
+ end
42
+
43
+ def replay(message_id)
44
+ res = @client.execute_request("POST", "/v1/webhooks/#{message_id}/replay")
45
+ ReplayOut.deserialize(res)
46
+ end
47
+ end
48
+ end