hooksniff 0.3.0 → 1.0.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 (209) 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/message.rb +48 -0
  12. data/lib/hooksniff/api/message_attempt.rb +38 -0
  13. data/lib/hooksniff/api/statistics.rb +37 -0
  14. data/lib/{openapi_client → hooksniff}/api_error.rb +8 -18
  15. data/lib/hooksniff/errors.rb +129 -0
  16. data/lib/hooksniff/hooksniff.rb +36 -0
  17. data/lib/hooksniff/hooksniff_http_client.rb +128 -0
  18. data/lib/hooksniff/http_error_out.rb +18 -0
  19. data/lib/hooksniff/http_validation_error.rb +18 -0
  20. data/lib/hooksniff/internal.rb +7 -0
  21. data/lib/hooksniff/models/aggregate_event_types_out.rb +59 -0
  22. data/lib/hooksniff/models/endpoint_created_event.rb +50 -0
  23. data/lib/hooksniff/models/endpoint_created_event_data.rb +63 -0
  24. data/lib/hooksniff/models/endpoint_deleted_event.rb +50 -0
  25. data/lib/hooksniff/models/endpoint_deleted_event_data.rb +63 -0
  26. data/lib/hooksniff/models/endpoint_disabled_event.rb +53 -0
  27. data/lib/hooksniff/models/endpoint_disabled_event_data.rb +69 -0
  28. data/lib/hooksniff/models/endpoint_enabled_event.rb +50 -0
  29. data/lib/hooksniff/models/endpoint_enabled_event_data.rb +63 -0
  30. data/lib/hooksniff/models/endpoint_headers_in.rb +46 -0
  31. data/lib/hooksniff/models/endpoint_headers_out.rb +52 -0
  32. data/lib/hooksniff/models/endpoint_headers_patch_in.rb +53 -0
  33. data/lib/hooksniff/models/endpoint_in.rb +102 -0
  34. data/lib/hooksniff/models/endpoint_out.rb +104 -0
  35. data/lib/hooksniff/models/endpoint_patch.rb +97 -0
  36. data/lib/hooksniff/models/endpoint_secret_out.rb +50 -0
  37. data/lib/hooksniff/models/endpoint_secret_rotate_in.rb +53 -0
  38. data/lib/hooksniff/models/endpoint_update.rb +90 -0
  39. data/lib/hooksniff/models/endpoint_updated_event.rb +50 -0
  40. data/lib/hooksniff/models/endpoint_updated_event_data.rb +63 -0
  41. data/lib/hooksniff/models/event_in.rb +50 -0
  42. data/lib/hooksniff/models/event_out.rb +53 -0
  43. data/lib/hooksniff/models/event_type_in.rb +80 -0
  44. data/lib/hooksniff/models/event_type_out.rb +87 -0
  45. data/lib/hooksniff/models/event_type_patch.rb +66 -0
  46. data/lib/hooksniff/models/event_type_update.rb +67 -0
  47. data/lib/hooksniff/models/list_response_endpoint_out.rb +58 -0
  48. data/lib/hooksniff/models/list_response_event_type_out.rb +58 -0
  49. data/lib/hooksniff/models/list_response_message_attempt_out.rb +58 -0
  50. data/lib/hooksniff/models/list_response_message_out.rb +58 -0
  51. data/lib/hooksniff/models/message_attempt_exhausted_event.rb +53 -0
  52. data/lib/hooksniff/models/message_attempt_exhausted_event_data.rb +70 -0
  53. data/lib/hooksniff/models/message_attempt_failed_data.rb +56 -0
  54. data/lib/hooksniff/models/message_attempt_failing_event.rb +54 -0
  55. data/lib/hooksniff/models/message_attempt_failing_event_data.rb +70 -0
  56. data/lib/hooksniff/models/message_attempt_log.rb +112 -0
  57. data/lib/hooksniff/models/message_attempt_log_event.rb +53 -0
  58. data/lib/hooksniff/models/message_attempt_out.rb +96 -0
  59. data/lib/hooksniff/models/message_attempt_recovered_event.rb +53 -0
  60. data/lib/hooksniff/models/message_attempt_recovered_event_data.rb +70 -0
  61. data/lib/hooksniff/models/message_attempt_trigger_type.rb +33 -0
  62. data/lib/hooksniff/models/message_endpoint_out.rb +112 -0
  63. data/lib/hooksniff/models/message_in.rb +100 -0
  64. data/lib/hooksniff/models/message_out.rb +71 -0
  65. data/lib/hooksniff/models/message_status.rb +39 -0
  66. data/lib/hooksniff/models/message_status_text.rb +32 -0
  67. data/lib/hooksniff/models/ordering.rb +30 -0
  68. data/lib/hooksniff/models/status_code_class.rb +41 -0
  69. data/lib/hooksniff/util.rb +69 -0
  70. data/lib/hooksniff/validation_error.rb +28 -0
  71. data/lib/hooksniff/version.rb +5 -0
  72. data/lib/hooksniff/webhook.rb +84 -0
  73. data/lib/hooksniff.rb +78 -0
  74. data/test/test_hooksniff.rb +86 -0
  75. metadata +127 -163
  76. data/lib/openapi_client/api/admin_api.rb +0 -452
  77. data/lib/openapi_client/api/alerts_api.rb +0 -322
  78. data/lib/openapi_client/api/analytics_api.rb +0 -208
  79. data/lib/openapi_client/api/api_keys_api.rb +0 -252
  80. data/lib/openapi_client/api/audit_log_api.rb +0 -140
  81. data/lib/openapi_client/api/auth_api.rb +0 -1080
  82. data/lib/openapi_client/api/billing_api.rb +0 -500
  83. data/lib/openapi_client/api/contact_api.rb +0 -88
  84. data/lib/openapi_client/api/custom_domains_api.rb +0 -253
  85. data/lib/openapi_client/api/customer_portal_api.rb +0 -700
  86. data/lib/openapi_client/api/delivery_details_api.rb +0 -146
  87. data/lib/openapi_client/api/devices_api.rb +0 -202
  88. data/lib/openapi_client/api/embed_api.rb +0 -128
  89. data/lib/openapi_client/api/endpoints_api.rb +0 -468
  90. data/lib/openapi_client/api/events_api.rb +0 -75
  91. data/lib/openapi_client/api/health_api.rb +0 -193
  92. data/lib/openapi_client/api/inbound_api.rb +0 -170
  93. data/lib/openapi_client/api/notifications_api.rb +0 -309
  94. data/lib/openapi_client/api/o_auth_api.rb +0 -181
  95. data/lib/openapi_client/api/outbound_ips_api.rb +0 -77
  96. data/lib/openapi_client/api/playground_api.rb +0 -143
  97. data/lib/openapi_client/api/rate_limits_api.rb +0 -252
  98. data/lib/openapi_client/api/routing_api.rb +0 -393
  99. data/lib/openapi_client/api/schemas_api.rb +0 -268
  100. data/lib/openapi_client/api/search_api.rb +0 -96
  101. data/lib/openapi_client/api/simulator_api.rb +0 -82
  102. data/lib/openapi_client/api/sso_api.rb +0 -241
  103. data/lib/openapi_client/api/stats_api.rb +0 -77
  104. data/lib/openapi_client/api/stream_api.rb +0 -88
  105. data/lib/openapi_client/api/teams_api.rb +0 -476
  106. data/lib/openapi_client/api/templates_api.rb +0 -213
  107. data/lib/openapi_client/api/transforms_api.rb +0 -368
  108. data/lib/openapi_client/api/webhooks_api.rb +0 -534
  109. data/lib/openapi_client/api_client.rb +0 -397
  110. data/lib/openapi_client/api_model_base.rb +0 -88
  111. data/lib/openapi_client/configuration.rb +0 -312
  112. data/lib/openapi_client/models/admin_revenue_get200_response_inner.rb +0 -165
  113. data/lib/openapi_client/models/admin_sdk_update_post_request.rb +0 -156
  114. data/lib/openapi_client/models/admin_users_id_plan_put_request.rb +0 -181
  115. data/lib/openapi_client/models/admin_users_id_status_put_request.rb +0 -147
  116. data/lib/openapi_client/models/alert_rule.rb +0 -237
  117. data/lib/openapi_client/models/api_key_info.rb +0 -185
  118. data/lib/openapi_client/models/apply_template_request.rb +0 -173
  119. data/lib/openapi_client/models/apply_template_response.rb +0 -156
  120. data/lib/openapi_client/models/auth2fa_enable_post200_response.rb +0 -156
  121. data/lib/openapi_client/models/auth_login_post200_response.rb +0 -104
  122. data/lib/openapi_client/models/auth_response.rb +0 -167
  123. data/lib/openapi_client/models/batch_replay_request.rb +0 -166
  124. data/lib/openapi_client/models/batch_response.rb +0 -160
  125. data/lib/openapi_client/models/batch_response_errors_inner.rb +0 -156
  126. data/lib/openapi_client/models/batch_webhook_request.rb +0 -166
  127. data/lib/openapi_client/models/billing_portal_post200_response.rb +0 -147
  128. data/lib/openapi_client/models/change_password_request.rb +0 -199
  129. data/lib/openapi_client/models/change_role_request.rb +0 -188
  130. data/lib/openapi_client/models/confirm2fa_request.rb +0 -182
  131. data/lib/openapi_client/models/contact_request.rb +0 -242
  132. data/lib/openapi_client/models/contact_response.rb +0 -156
  133. data/lib/openapi_client/models/create_alert_request.rb +0 -277
  134. data/lib/openapi_client/models/create_api_key_response.rb +0 -175
  135. data/lib/openapi_client/models/create_endpoint_request.rb +0 -288
  136. data/lib/openapi_client/models/create_team_request.rb +0 -164
  137. data/lib/openapi_client/models/create_transform_rule_request.rb +0 -216
  138. data/lib/openapi_client/models/create_webhook_request.rb +0 -201
  139. data/lib/openapi_client/models/custom_domains_post_request.rb +0 -147
  140. data/lib/openapi_client/models/customer_response.rb +0 -256
  141. data/lib/openapi_client/models/delivery.rb +0 -246
  142. data/lib/openapi_client/models/delivery_attempt.rb +0 -205
  143. data/lib/openapi_client/models/delivery_list_response.rb +0 -176
  144. data/lib/openapi_client/models/delivery_trend_response.rb +0 -158
  145. data/lib/openapi_client/models/delivery_trend_response_buckets_inner.rb +0 -174
  146. data/lib/openapi_client/models/device_token_response.rb +0 -174
  147. data/lib/openapi_client/models/disable2fa_request.rb +0 -164
  148. data/lib/openapi_client/models/enable2fa_request.rb +0 -164
  149. data/lib/openapi_client/models/endpoint.rb +0 -321
  150. data/lib/openapi_client/models/endpoint_health.rb +0 -183
  151. data/lib/openapi_client/models/endpoints_endpoint_id_transforms_test_post_request.rb +0 -156
  152. data/lib/openapi_client/models/endpoints_id_rotate_secret_post200_response.rb +0 -156
  153. data/lib/openapi_client/models/error.rb +0 -165
  154. data/lib/openapi_client/models/forgot_password_request.rb +0 -164
  155. data/lib/openapi_client/models/invite_request.rb +0 -207
  156. data/lib/openapi_client/models/invoice_response.rb +0 -183
  157. data/lib/openapi_client/models/latency_trend_response.rb +0 -167
  158. data/lib/openapi_client/models/latency_trend_response_buckets_inner.rb +0 -165
  159. data/lib/openapi_client/models/login_request.rb +0 -190
  160. data/lib/openapi_client/models/notification.rb +0 -193
  161. data/lib/openapi_client/models/notification_list_response.rb +0 -167
  162. data/lib/openapi_client/models/notification_preferences.rb +0 -201
  163. data/lib/openapi_client/models/notifications_unread_count_get200_response.rb +0 -147
  164. data/lib/openapi_client/models/outbound_ips_response.rb +0 -158
  165. data/lib/openapi_client/models/paginated_users.rb +0 -176
  166. data/lib/openapi_client/models/playground_get200_response.rb +0 -160
  167. data/lib/openapi_client/models/portal_notifications_put200_response.rb +0 -156
  168. data/lib/openapi_client/models/portal_profile.rb +0 -184
  169. data/lib/openapi_client/models/refresh_token_request.rb +0 -164
  170. data/lib/openapi_client/models/register_device_request.rb +0 -208
  171. data/lib/openapi_client/models/register_request.rb +0 -201
  172. data/lib/openapi_client/models/register_schema_request.rb +0 -191
  173. data/lib/openapi_client/models/resend_verification_request.rb +0 -164
  174. data/lib/openapi_client/models/reset_password_request.rb +0 -199
  175. data/lib/openapi_client/models/retry_policy.rb +0 -216
  176. data/lib/openapi_client/models/routing_info.rb +0 -193
  177. data/lib/openapi_client/models/search_result.rb +0 -158
  178. data/lib/openapi_client/models/simulator_post_request.rb +0 -165
  179. data/lib/openapi_client/models/sso_config_post_request.rb +0 -190
  180. data/lib/openapi_client/models/stats_response.rb +0 -210
  181. data/lib/openapi_client/models/stream_params.rb +0 -201
  182. data/lib/openapi_client/models/subscription_response.rb +0 -201
  183. data/lib/openapi_client/models/success_rate_response.rb +0 -183
  184. data/lib/openapi_client/models/system_stats.rb +0 -185
  185. data/lib/openapi_client/models/system_stats_plan_breakdown_inner.rb +0 -156
  186. data/lib/openapi_client/models/system_status.rb +0 -210
  187. data/lib/openapi_client/models/system_status_components_inner.rb +0 -184
  188. data/lib/openapi_client/models/team.rb +0 -165
  189. data/lib/openapi_client/models/team_detail_response.rb +0 -169
  190. data/lib/openapi_client/models/team_invite.rb +0 -174
  191. data/lib/openapi_client/models/team_member.rb +0 -193
  192. data/lib/openapi_client/models/test_webhook_request.rb +0 -199
  193. data/lib/openapi_client/models/test_webhook_response.rb +0 -174
  194. data/lib/openapi_client/models/transform_rule.rb +0 -201
  195. data/lib/openapi_client/models/two_factor_required_response.rb +0 -165
  196. data/lib/openapi_client/models/update_endpoint_request.rb +0 -278
  197. data/lib/openapi_client/models/update_notification_preferences.rb +0 -195
  198. data/lib/openapi_client/models/update_profile_request.rb +0 -190
  199. data/lib/openapi_client/models/update_routing_request.rb +0 -190
  200. data/lib/openapi_client/models/upgrade_request.rb +0 -209
  201. data/lib/openapi_client/models/upgrade_response.rb +0 -166
  202. data/lib/openapi_client/models/usage_response.rb +0 -201
  203. data/lib/openapi_client/models/user_summary.rb +0 -193
  204. data/lib/openapi_client/models/validate_event_request.rb +0 -164
  205. data/lib/openapi_client/models/verify2fa_request.rb +0 -208
  206. data/lib/openapi_client/models/verify_email_request.rb +0 -164
  207. data/lib/openapi_client/models/webhook_template.rb +0 -183
  208. data/lib/openapi_client/version.rb +0 -15
  209. 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: b5aaa88a65699339eb037c794a5d39c3cf2a7aecd9f51536c66f724ceb167e63
4
+ data.tar.gz: 8fc8649416effe14346d24ef9d9853f84b47f172f4e8521761f5a386b1017a47
5
5
  SHA512:
6
- metadata.gz: 0fc3db5d623e798a34e9983930a97aa8a05dd319cf48004d34dc4fa5dfeee54d6c40a0350469950ba985cd77ee7e5d4ca4b11757d051540036db213e96920610
7
- data.tar.gz: ca1157a3ec1a9384ad2a2fae4fe185f0b761f4946cbcbc0eef435ba9d410f9b3f2724046edaa6980970e583c72b7640d71e77c42f2f2f6ab72bc0e235dcc5b8c
6
+ metadata.gz: 28cf8842859a1b34b5fa563d8e076845d2657d956c4bcde51be444ee250aaaedae8814eb354130884a733ba6e9b9de5cfac3883d63da697f5db113973806362f
7
+ data.tar.gz: f3d265478cc4bba62e1eb27631da361d3249529d45ab06111babd9adbdd57223b954c23e49b5cdea9671d3c62067ea89254c19948b3b1acf66c62333f7a907c4
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,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
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+
5
+ module HookSniff
6
+ class MessageAttempt
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def list_by_msg(message_id, options = {})
12
+ options = options.transform_keys(&:to_s)
13
+ res = @client.execute_request(
14
+ "GET",
15
+ "/v1/webhooks/#{message_id}/attempts",
16
+ query_params: {
17
+ "limit" => options["limit"],
18
+ "offset" => options["offset"],
19
+ "status" => options["status"]
20
+ }
21
+ )
22
+ ListResponseMessageAttemptOut.deserialize(res)
23
+ end
24
+
25
+ def get(attempt_id)
26
+ res = @client.execute_request("GET", "/v1/webhooks/attempts/#{attempt_id}")
27
+ MessageAttemptOut.deserialize(res)
28
+ end
29
+
30
+ def resend(message_id, endpoint_id)
31
+ res = @client.execute_request(
32
+ "POST",
33
+ "/v1/webhooks/#{message_id}/attempts/#{endpoint_id}/resend"
34
+ )
35
+ nil
36
+ end
37
+ end
38
+ end