webhookdb 0.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 (364) hide show
  1. checksums.yaml +7 -0
  2. data/data/messages/layouts/blank.email.liquid +10 -0
  3. data/data/messages/layouts/minimal.email.liquid +28 -0
  4. data/data/messages/layouts/standard.email.liquid +28 -0
  5. data/data/messages/partials/button.liquid +15 -0
  6. data/data/messages/partials/environment_banner.liquid +9 -0
  7. data/data/messages/partials/footer.liquid +22 -0
  8. data/data/messages/partials/greeting.liquid +3 -0
  9. data/data/messages/partials/logo_header.liquid +18 -0
  10. data/data/messages/partials/signoff.liquid +1 -0
  11. data/data/messages/styles/v1.liquid +346 -0
  12. data/data/messages/templates/errors/icalendar_fetch.email.liquid +29 -0
  13. data/data/messages/templates/invite.email.liquid +15 -0
  14. data/data/messages/templates/new_customer.email.liquid +24 -0
  15. data/data/messages/templates/org_database_migration_finished.email.liquid +7 -0
  16. data/data/messages/templates/org_database_migration_started.email.liquid +9 -0
  17. data/data/messages/templates/specs/_field_partial.liquid +1 -0
  18. data/data/messages/templates/specs/basic.email.liquid +2 -0
  19. data/data/messages/templates/specs/basic.fake.liquid +1 -0
  20. data/data/messages/templates/specs/with_field.email.liquid +2 -0
  21. data/data/messages/templates/specs/with_field.fake.liquid +1 -0
  22. data/data/messages/templates/specs/with_include.email.liquid +2 -0
  23. data/data/messages/templates/specs/with_partial.email.liquid +1 -0
  24. data/data/messages/templates/verification.email.liquid +14 -0
  25. data/data/messages/templates/verification.sms.liquid +1 -0
  26. data/data/messages/web/install-customer-login.liquid +48 -0
  27. data/data/messages/web/install-error.liquid +17 -0
  28. data/data/messages/web/install-success.liquid +35 -0
  29. data/data/messages/web/install.liquid +20 -0
  30. data/data/messages/web/partials/footer.liquid +4 -0
  31. data/data/messages/web/partials/form_error.liquid +1 -0
  32. data/data/messages/web/partials/header.liquid +3 -0
  33. data/data/messages/web/styles.liquid +134 -0
  34. data/data/windows_tz.txt +461 -0
  35. data/db/migrations/001_testing_pixies.rb +13 -0
  36. data/db/migrations/002_initial.rb +132 -0
  37. data/db/migrations/003_ux_overhaul.rb +20 -0
  38. data/db/migrations/004_incremental_backfill.rb +9 -0
  39. data/db/migrations/005_log_webhooks.rb +24 -0
  40. data/db/migrations/006_generalize_roles.rb +29 -0
  41. data/db/migrations/007_org_dns.rb +12 -0
  42. data/db/migrations/008_webhook_subscriptions.rb +19 -0
  43. data/db/migrations/009_nonunique_stripe_subscription_customer.rb +16 -0
  44. data/db/migrations/010_drop_integration_soft_delete.rb +14 -0
  45. data/db/migrations/011_webhook_subscriptions_created_at.rb +10 -0
  46. data/db/migrations/012_webhook_subscriptions_created_by.rb +9 -0
  47. data/db/migrations/013_default_org_membership.rb +30 -0
  48. data/db/migrations/014_webhook_subscription_deliveries.rb +26 -0
  49. data/db/migrations/015_dependent_integrations.rb +9 -0
  50. data/db/migrations/016_encrypted_columns.rb +9 -0
  51. data/db/migrations/017_skip_verification.rb +9 -0
  52. data/db/migrations/018_sync_targets.rb +25 -0
  53. data/db/migrations/019_org_schema.rb +9 -0
  54. data/db/migrations/020_org_database_migrations.rb +25 -0
  55. data/db/migrations/021_no_default_org_schema.rb +14 -0
  56. data/db/migrations/022_database_document.rb +15 -0
  57. data/db/migrations/023_sync_target_schema.rb +9 -0
  58. data/db/migrations/024_org_semaphore_jobs.rb +9 -0
  59. data/db/migrations/025_integration_backfill_cursor.rb +9 -0
  60. data/db/migrations/026_undo_integration_backfill_cursor.rb +9 -0
  61. data/db/migrations/027_sync_target_http_sync.rb +12 -0
  62. data/db/migrations/028_logged_webhook_path.rb +24 -0
  63. data/db/migrations/029_encrypt_columns.rb +97 -0
  64. data/db/migrations/030_org_sync_target_timeout.rb +9 -0
  65. data/db/migrations/031_org_max_query_rows.rb +9 -0
  66. data/db/migrations/032_remove_db_defaults.rb +12 -0
  67. data/db/migrations/033_backfill_jobs.rb +26 -0
  68. data/db/migrations/034_backfill_job_criteria.rb +9 -0
  69. data/db/migrations/035_synchronous_backfill.rb +9 -0
  70. data/db/migrations/036_oauth.rb +26 -0
  71. data/db/migrations/037_oauth_used.rb +9 -0
  72. data/lib/amigo/durable_job.rb +416 -0
  73. data/lib/pry/clipboard.rb +111 -0
  74. data/lib/sequel/advisory_lock.rb +65 -0
  75. data/lib/webhookdb/admin.rb +4 -0
  76. data/lib/webhookdb/admin_api/auth.rb +36 -0
  77. data/lib/webhookdb/admin_api/customers.rb +63 -0
  78. data/lib/webhookdb/admin_api/database_documents.rb +20 -0
  79. data/lib/webhookdb/admin_api/entities.rb +66 -0
  80. data/lib/webhookdb/admin_api/message_deliveries.rb +61 -0
  81. data/lib/webhookdb/admin_api/roles.rb +15 -0
  82. data/lib/webhookdb/admin_api.rb +34 -0
  83. data/lib/webhookdb/aggregate_result.rb +63 -0
  84. data/lib/webhookdb/api/auth.rb +122 -0
  85. data/lib/webhookdb/api/connstr_auth.rb +36 -0
  86. data/lib/webhookdb/api/db.rb +188 -0
  87. data/lib/webhookdb/api/demo.rb +14 -0
  88. data/lib/webhookdb/api/entities.rb +198 -0
  89. data/lib/webhookdb/api/helpers.rb +253 -0
  90. data/lib/webhookdb/api/install.rb +296 -0
  91. data/lib/webhookdb/api/me.rb +53 -0
  92. data/lib/webhookdb/api/organizations.rb +254 -0
  93. data/lib/webhookdb/api/replay.rb +64 -0
  94. data/lib/webhookdb/api/service_integrations.rb +402 -0
  95. data/lib/webhookdb/api/services.rb +27 -0
  96. data/lib/webhookdb/api/stripe.rb +22 -0
  97. data/lib/webhookdb/api/subscriptions.rb +67 -0
  98. data/lib/webhookdb/api/sync_targets.rb +232 -0
  99. data/lib/webhookdb/api/system.rb +37 -0
  100. data/lib/webhookdb/api/webhook_subscriptions.rb +96 -0
  101. data/lib/webhookdb/api.rb +92 -0
  102. data/lib/webhookdb/apps.rb +93 -0
  103. data/lib/webhookdb/async/audit_logger.rb +38 -0
  104. data/lib/webhookdb/async/autoscaler.rb +84 -0
  105. data/lib/webhookdb/async/job.rb +18 -0
  106. data/lib/webhookdb/async/job_logger.rb +45 -0
  107. data/lib/webhookdb/async/scheduled_job.rb +18 -0
  108. data/lib/webhookdb/async.rb +142 -0
  109. data/lib/webhookdb/aws.rb +98 -0
  110. data/lib/webhookdb/backfill_job.rb +107 -0
  111. data/lib/webhookdb/backfiller.rb +107 -0
  112. data/lib/webhookdb/cloudflare.rb +39 -0
  113. data/lib/webhookdb/connection_cache.rb +177 -0
  114. data/lib/webhookdb/console.rb +71 -0
  115. data/lib/webhookdb/convertkit.rb +14 -0
  116. data/lib/webhookdb/crypto.rb +66 -0
  117. data/lib/webhookdb/customer/reset_code.rb +94 -0
  118. data/lib/webhookdb/customer.rb +347 -0
  119. data/lib/webhookdb/database_document.rb +72 -0
  120. data/lib/webhookdb/db_adapter/column_types.rb +37 -0
  121. data/lib/webhookdb/db_adapter/default_sql.rb +187 -0
  122. data/lib/webhookdb/db_adapter/pg.rb +96 -0
  123. data/lib/webhookdb/db_adapter/snowflake.rb +137 -0
  124. data/lib/webhookdb/db_adapter.rb +208 -0
  125. data/lib/webhookdb/dbutil.rb +92 -0
  126. data/lib/webhookdb/demo_mode.rb +100 -0
  127. data/lib/webhookdb/developer_alert.rb +51 -0
  128. data/lib/webhookdb/email_octopus.rb +21 -0
  129. data/lib/webhookdb/enumerable.rb +18 -0
  130. data/lib/webhookdb/fixtures/backfill_jobs.rb +72 -0
  131. data/lib/webhookdb/fixtures/customers.rb +65 -0
  132. data/lib/webhookdb/fixtures/database_documents.rb +27 -0
  133. data/lib/webhookdb/fixtures/faker.rb +41 -0
  134. data/lib/webhookdb/fixtures/logged_webhooks.rb +56 -0
  135. data/lib/webhookdb/fixtures/message_deliveries.rb +59 -0
  136. data/lib/webhookdb/fixtures/oauth_sessions.rb +24 -0
  137. data/lib/webhookdb/fixtures/organization_database_migrations.rb +37 -0
  138. data/lib/webhookdb/fixtures/organization_memberships.rb +54 -0
  139. data/lib/webhookdb/fixtures/organizations.rb +32 -0
  140. data/lib/webhookdb/fixtures/reset_codes.rb +23 -0
  141. data/lib/webhookdb/fixtures/service_integrations.rb +42 -0
  142. data/lib/webhookdb/fixtures/subscriptions.rb +33 -0
  143. data/lib/webhookdb/fixtures/sync_targets.rb +32 -0
  144. data/lib/webhookdb/fixtures/webhook_subscriptions.rb +35 -0
  145. data/lib/webhookdb/fixtures.rb +15 -0
  146. data/lib/webhookdb/formatting.rb +56 -0
  147. data/lib/webhookdb/front.rb +49 -0
  148. data/lib/webhookdb/github.rb +22 -0
  149. data/lib/webhookdb/google_calendar.rb +29 -0
  150. data/lib/webhookdb/heroku.rb +21 -0
  151. data/lib/webhookdb/http.rb +114 -0
  152. data/lib/webhookdb/icalendar.rb +17 -0
  153. data/lib/webhookdb/id.rb +17 -0
  154. data/lib/webhookdb/idempotency.rb +90 -0
  155. data/lib/webhookdb/increase.rb +42 -0
  156. data/lib/webhookdb/intercom.rb +23 -0
  157. data/lib/webhookdb/jobs/amigo_test_jobs.rb +118 -0
  158. data/lib/webhookdb/jobs/backfill.rb +32 -0
  159. data/lib/webhookdb/jobs/create_mirror_table.rb +18 -0
  160. data/lib/webhookdb/jobs/create_stripe_customer.rb +17 -0
  161. data/lib/webhookdb/jobs/customer_created_notify_internal.rb +22 -0
  162. data/lib/webhookdb/jobs/demo_mode_sync_data.rb +19 -0
  163. data/lib/webhookdb/jobs/deprecated_jobs.rb +19 -0
  164. data/lib/webhookdb/jobs/developer_alert_handle.rb +14 -0
  165. data/lib/webhookdb/jobs/durable_job_recheck_poller.rb +17 -0
  166. data/lib/webhookdb/jobs/emailer.rb +15 -0
  167. data/lib/webhookdb/jobs/icalendar_enqueue_syncs.rb +25 -0
  168. data/lib/webhookdb/jobs/icalendar_sync.rb +23 -0
  169. data/lib/webhookdb/jobs/logged_webhook_replay.rb +17 -0
  170. data/lib/webhookdb/jobs/logged_webhook_resilient_replay.rb +15 -0
  171. data/lib/webhookdb/jobs/message_dispatched.rb +16 -0
  172. data/lib/webhookdb/jobs/organization_database_migration_notify_finished.rb +21 -0
  173. data/lib/webhookdb/jobs/organization_database_migration_notify_started.rb +21 -0
  174. data/lib/webhookdb/jobs/organization_database_migration_run.rb +24 -0
  175. data/lib/webhookdb/jobs/prepare_database_connections.rb +22 -0
  176. data/lib/webhookdb/jobs/process_webhook.rb +47 -0
  177. data/lib/webhookdb/jobs/renew_watch_channel.rb +24 -0
  178. data/lib/webhookdb/jobs/replication_migration.rb +24 -0
  179. data/lib/webhookdb/jobs/reset_code_create_dispatch.rb +23 -0
  180. data/lib/webhookdb/jobs/scheduled_backfills.rb +77 -0
  181. data/lib/webhookdb/jobs/send_invite.rb +15 -0
  182. data/lib/webhookdb/jobs/send_test_webhook.rb +25 -0
  183. data/lib/webhookdb/jobs/send_webhook.rb +20 -0
  184. data/lib/webhookdb/jobs/sync_target_enqueue_scheduled.rb +16 -0
  185. data/lib/webhookdb/jobs/sync_target_run_sync.rb +38 -0
  186. data/lib/webhookdb/jobs/trim_logged_webhooks.rb +15 -0
  187. data/lib/webhookdb/jobs/webhook_resource_notify_integrations.rb +30 -0
  188. data/lib/webhookdb/jobs/webhook_subscription_delivery_attempt.rb +29 -0
  189. data/lib/webhookdb/jobs.rb +4 -0
  190. data/lib/webhookdb/json.rb +113 -0
  191. data/lib/webhookdb/liquid/expose.rb +27 -0
  192. data/lib/webhookdb/liquid/filters.rb +16 -0
  193. data/lib/webhookdb/liquid/liquification.rb +26 -0
  194. data/lib/webhookdb/liquid/partial.rb +12 -0
  195. data/lib/webhookdb/logged_webhook/resilient.rb +95 -0
  196. data/lib/webhookdb/logged_webhook.rb +194 -0
  197. data/lib/webhookdb/message/body.rb +25 -0
  198. data/lib/webhookdb/message/delivery.rb +127 -0
  199. data/lib/webhookdb/message/email_transport.rb +133 -0
  200. data/lib/webhookdb/message/fake_transport.rb +54 -0
  201. data/lib/webhookdb/message/liquid_drops.rb +29 -0
  202. data/lib/webhookdb/message/template.rb +89 -0
  203. data/lib/webhookdb/message/transport.rb +43 -0
  204. data/lib/webhookdb/message.rb +150 -0
  205. data/lib/webhookdb/messages/error_icalendar_fetch.rb +42 -0
  206. data/lib/webhookdb/messages/invite.rb +23 -0
  207. data/lib/webhookdb/messages/new_customer.rb +14 -0
  208. data/lib/webhookdb/messages/org_database_migration_finished.rb +23 -0
  209. data/lib/webhookdb/messages/org_database_migration_started.rb +24 -0
  210. data/lib/webhookdb/messages/specs.rb +57 -0
  211. data/lib/webhookdb/messages/verification.rb +23 -0
  212. data/lib/webhookdb/method_utilities.rb +82 -0
  213. data/lib/webhookdb/microsoft_calendar.rb +36 -0
  214. data/lib/webhookdb/nextpax.rb +14 -0
  215. data/lib/webhookdb/oauth/front.rb +58 -0
  216. data/lib/webhookdb/oauth/intercom.rb +58 -0
  217. data/lib/webhookdb/oauth/session.rb +24 -0
  218. data/lib/webhookdb/oauth.rb +80 -0
  219. data/lib/webhookdb/organization/alerting.rb +35 -0
  220. data/lib/webhookdb/organization/database_migration.rb +151 -0
  221. data/lib/webhookdb/organization/db_builder.rb +429 -0
  222. data/lib/webhookdb/organization.rb +506 -0
  223. data/lib/webhookdb/organization_membership.rb +58 -0
  224. data/lib/webhookdb/phone_number.rb +38 -0
  225. data/lib/webhookdb/plaid.rb +23 -0
  226. data/lib/webhookdb/platform.rb +27 -0
  227. data/lib/webhookdb/plivo.rb +52 -0
  228. data/lib/webhookdb/postgres/maintenance.rb +166 -0
  229. data/lib/webhookdb/postgres/model.rb +82 -0
  230. data/lib/webhookdb/postgres/model_utilities.rb +382 -0
  231. data/lib/webhookdb/postgres/testing_pixie.rb +16 -0
  232. data/lib/webhookdb/postgres/validations.rb +46 -0
  233. data/lib/webhookdb/postgres.rb +176 -0
  234. data/lib/webhookdb/postmark.rb +20 -0
  235. data/lib/webhookdb/redis.rb +35 -0
  236. data/lib/webhookdb/replicator/atom_single_feed_v1.rb +116 -0
  237. data/lib/webhookdb/replicator/aws_pricing_v1.rb +488 -0
  238. data/lib/webhookdb/replicator/base.rb +1185 -0
  239. data/lib/webhookdb/replicator/column.rb +482 -0
  240. data/lib/webhookdb/replicator/convertkit_broadcast_v1.rb +69 -0
  241. data/lib/webhookdb/replicator/convertkit_subscriber_v1.rb +200 -0
  242. data/lib/webhookdb/replicator/convertkit_tag_v1.rb +66 -0
  243. data/lib/webhookdb/replicator/convertkit_v1_mixin.rb +65 -0
  244. data/lib/webhookdb/replicator/docgen.rb +167 -0
  245. data/lib/webhookdb/replicator/email_octopus_campaign_v1.rb +84 -0
  246. data/lib/webhookdb/replicator/email_octopus_contact_v1.rb +159 -0
  247. data/lib/webhookdb/replicator/email_octopus_event_v1.rb +244 -0
  248. data/lib/webhookdb/replicator/email_octopus_list_v1.rb +101 -0
  249. data/lib/webhookdb/replicator/fake.rb +453 -0
  250. data/lib/webhookdb/replicator/front_conversation_v1.rb +45 -0
  251. data/lib/webhookdb/replicator/front_marketplace_root_v1.rb +55 -0
  252. data/lib/webhookdb/replicator/front_message_v1.rb +45 -0
  253. data/lib/webhookdb/replicator/front_v1_mixin.rb +22 -0
  254. data/lib/webhookdb/replicator/github_issue_comment_v1.rb +58 -0
  255. data/lib/webhookdb/replicator/github_issue_v1.rb +83 -0
  256. data/lib/webhookdb/replicator/github_pull_v1.rb +84 -0
  257. data/lib/webhookdb/replicator/github_release_v1.rb +47 -0
  258. data/lib/webhookdb/replicator/github_repo_v1_mixin.rb +250 -0
  259. data/lib/webhookdb/replicator/github_repository_event_v1.rb +45 -0
  260. data/lib/webhookdb/replicator/icalendar_calendar_v1.rb +465 -0
  261. data/lib/webhookdb/replicator/icalendar_event_v1.rb +334 -0
  262. data/lib/webhookdb/replicator/increase_account_number_v1.rb +77 -0
  263. data/lib/webhookdb/replicator/increase_account_transfer_v1.rb +61 -0
  264. data/lib/webhookdb/replicator/increase_account_v1.rb +63 -0
  265. data/lib/webhookdb/replicator/increase_ach_transfer_v1.rb +78 -0
  266. data/lib/webhookdb/replicator/increase_check_transfer_v1.rb +64 -0
  267. data/lib/webhookdb/replicator/increase_limit_v1.rb +78 -0
  268. data/lib/webhookdb/replicator/increase_transaction_v1.rb +74 -0
  269. data/lib/webhookdb/replicator/increase_v1_mixin.rb +121 -0
  270. data/lib/webhookdb/replicator/increase_wire_transfer_v1.rb +61 -0
  271. data/lib/webhookdb/replicator/intercom_contact_v1.rb +36 -0
  272. data/lib/webhookdb/replicator/intercom_conversation_v1.rb +38 -0
  273. data/lib/webhookdb/replicator/intercom_marketplace_root_v1.rb +69 -0
  274. data/lib/webhookdb/replicator/intercom_v1_mixin.rb +105 -0
  275. data/lib/webhookdb/replicator/oauth_refresh_access_token_mixin.rb +65 -0
  276. data/lib/webhookdb/replicator/plivo_sms_inbound_v1.rb +102 -0
  277. data/lib/webhookdb/replicator/postmark_inbound_message_v1.rb +94 -0
  278. data/lib/webhookdb/replicator/postmark_outbound_message_event_v1.rb +107 -0
  279. data/lib/webhookdb/replicator/schema_modification.rb +42 -0
  280. data/lib/webhookdb/replicator/shopify_customer_v1.rb +58 -0
  281. data/lib/webhookdb/replicator/shopify_order_v1.rb +64 -0
  282. data/lib/webhookdb/replicator/shopify_v1_mixin.rb +161 -0
  283. data/lib/webhookdb/replicator/signalwire_message_v1.rb +169 -0
  284. data/lib/webhookdb/replicator/sponsy_customer_v1.rb +54 -0
  285. data/lib/webhookdb/replicator/sponsy_placement_v1.rb +34 -0
  286. data/lib/webhookdb/replicator/sponsy_publication_v1.rb +125 -0
  287. data/lib/webhookdb/replicator/sponsy_slot_v1.rb +41 -0
  288. data/lib/webhookdb/replicator/sponsy_status_v1.rb +35 -0
  289. data/lib/webhookdb/replicator/sponsy_v1_mixin.rb +165 -0
  290. data/lib/webhookdb/replicator/state_machine_step.rb +69 -0
  291. data/lib/webhookdb/replicator/stripe_charge_v1.rb +77 -0
  292. data/lib/webhookdb/replicator/stripe_coupon_v1.rb +62 -0
  293. data/lib/webhookdb/replicator/stripe_customer_v1.rb +60 -0
  294. data/lib/webhookdb/replicator/stripe_dispute_v1.rb +77 -0
  295. data/lib/webhookdb/replicator/stripe_invoice_item_v1.rb +82 -0
  296. data/lib/webhookdb/replicator/stripe_invoice_v1.rb +116 -0
  297. data/lib/webhookdb/replicator/stripe_payout_v1.rb +67 -0
  298. data/lib/webhookdb/replicator/stripe_price_v1.rb +60 -0
  299. data/lib/webhookdb/replicator/stripe_product_v1.rb +60 -0
  300. data/lib/webhookdb/replicator/stripe_refund_v1.rb +101 -0
  301. data/lib/webhookdb/replicator/stripe_subscription_item_v1.rb +56 -0
  302. data/lib/webhookdb/replicator/stripe_subscription_v1.rb +75 -0
  303. data/lib/webhookdb/replicator/stripe_v1_mixin.rb +116 -0
  304. data/lib/webhookdb/replicator/transistor_episode_stats_v1.rb +141 -0
  305. data/lib/webhookdb/replicator/transistor_episode_v1.rb +169 -0
  306. data/lib/webhookdb/replicator/transistor_show_v1.rb +68 -0
  307. data/lib/webhookdb/replicator/transistor_v1_mixin.rb +65 -0
  308. data/lib/webhookdb/replicator/twilio_sms_v1.rb +156 -0
  309. data/lib/webhookdb/replicator/webhook_request.rb +5 -0
  310. data/lib/webhookdb/replicator/webhookdb_customer_v1.rb +74 -0
  311. data/lib/webhookdb/replicator.rb +224 -0
  312. data/lib/webhookdb/role.rb +42 -0
  313. data/lib/webhookdb/sentry.rb +35 -0
  314. data/lib/webhookdb/service/auth.rb +138 -0
  315. data/lib/webhookdb/service/collection.rb +91 -0
  316. data/lib/webhookdb/service/entities.rb +97 -0
  317. data/lib/webhookdb/service/helpers.rb +270 -0
  318. data/lib/webhookdb/service/middleware.rb +124 -0
  319. data/lib/webhookdb/service/types.rb +30 -0
  320. data/lib/webhookdb/service/validators.rb +32 -0
  321. data/lib/webhookdb/service/view_api.rb +63 -0
  322. data/lib/webhookdb/service.rb +219 -0
  323. data/lib/webhookdb/service_integration.rb +332 -0
  324. data/lib/webhookdb/shopify.rb +35 -0
  325. data/lib/webhookdb/signalwire.rb +13 -0
  326. data/lib/webhookdb/slack.rb +68 -0
  327. data/lib/webhookdb/snowflake.rb +90 -0
  328. data/lib/webhookdb/spec_helpers/async.rb +122 -0
  329. data/lib/webhookdb/spec_helpers/citest.rb +88 -0
  330. data/lib/webhookdb/spec_helpers/integration.rb +121 -0
  331. data/lib/webhookdb/spec_helpers/message.rb +41 -0
  332. data/lib/webhookdb/spec_helpers/postgres.rb +220 -0
  333. data/lib/webhookdb/spec_helpers/service.rb +432 -0
  334. data/lib/webhookdb/spec_helpers/shared_examples_for_columns.rb +56 -0
  335. data/lib/webhookdb/spec_helpers/shared_examples_for_replicators.rb +915 -0
  336. data/lib/webhookdb/spec_helpers/whdb.rb +139 -0
  337. data/lib/webhookdb/spec_helpers.rb +63 -0
  338. data/lib/webhookdb/sponsy.rb +14 -0
  339. data/lib/webhookdb/stripe.rb +37 -0
  340. data/lib/webhookdb/subscription.rb +203 -0
  341. data/lib/webhookdb/sync_target.rb +491 -0
  342. data/lib/webhookdb/tasks/admin.rb +49 -0
  343. data/lib/webhookdb/tasks/annotate.rb +36 -0
  344. data/lib/webhookdb/tasks/db.rb +82 -0
  345. data/lib/webhookdb/tasks/docs.rb +42 -0
  346. data/lib/webhookdb/tasks/fixture.rb +35 -0
  347. data/lib/webhookdb/tasks/message.rb +50 -0
  348. data/lib/webhookdb/tasks/regress.rb +87 -0
  349. data/lib/webhookdb/tasks/release.rb +27 -0
  350. data/lib/webhookdb/tasks/sidekiq.rb +23 -0
  351. data/lib/webhookdb/tasks/specs.rb +64 -0
  352. data/lib/webhookdb/theranest.rb +15 -0
  353. data/lib/webhookdb/transistor.rb +13 -0
  354. data/lib/webhookdb/twilio.rb +13 -0
  355. data/lib/webhookdb/typed_struct.rb +44 -0
  356. data/lib/webhookdb/version.rb +5 -0
  357. data/lib/webhookdb/webhook_response.rb +50 -0
  358. data/lib/webhookdb/webhook_subscription/delivery.rb +82 -0
  359. data/lib/webhookdb/webhook_subscription.rb +226 -0
  360. data/lib/webhookdb/windows_tz.rb +32 -0
  361. data/lib/webhookdb/xml.rb +92 -0
  362. data/lib/webhookdb.rb +224 -0
  363. data/lib/webterm/apps.rb +45 -0
  364. metadata +1129 -0
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faker"
4
+ require "fluent_fixtures"
5
+
6
+ require "webhookdb"
7
+
8
+ module Webhookdb::Fixtures
9
+ extend FluentFixtures::Collection
10
+
11
+ # Set the path to use when finding fixtures for this collection
12
+ fixture_path_prefix "webhookdb/fixtures"
13
+
14
+ ::Faker::Config.locale = :en
15
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Webhookdb::Formatting
4
+ def self.blocks
5
+ return Blocks.new
6
+ end
7
+
8
+ class Blocks
9
+ def initialize
10
+ @arr = []
11
+ end
12
+
13
+ def blank
14
+ return self.line("")
15
+ end
16
+
17
+ def line(value)
18
+ @arr << Line.new(value)
19
+ return self
20
+ end
21
+
22
+ def table(headers, rows)
23
+ @arr << Table.new(headers, rows)
24
+ return self
25
+ end
26
+
27
+ def as_json(*a)
28
+ return @arr.as_json(*a)
29
+ end
30
+ end
31
+
32
+ class Line
33
+ attr_accessor :value
34
+
35
+ def initialize(value)
36
+ @value = value
37
+ end
38
+
39
+ def as_json(*)
40
+ return {type: "line", value: self.value}
41
+ end
42
+ end
43
+
44
+ class Table
45
+ attr_accessor :headers, :rows
46
+
47
+ def initialize(headers, rows)
48
+ @headers = headers
49
+ @rows = rows
50
+ end
51
+
52
+ def as_json(*o)
53
+ return {type: "table", value: {headers: self.headers, rows: self.rows.as_json(*o)}}
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+ require "appydays/loggable"
5
+
6
+ module Webhookdb::Front
7
+ include Appydays::Configurable
8
+
9
+ configurable(:front) do
10
+ # The api secret is used for webhook verification, the client id and secret are used for OAuth
11
+ setting :api_secret, "front_api_secret"
12
+ setting :client_id, "front_client_id"
13
+ setting :client_secret, "front_client_secret"
14
+ setting :http_timeout, 30
15
+ end
16
+
17
+ def self.oauth_callback_url = Webhookdb.api_url + "/v1/install/front/callback"
18
+
19
+ def self.verify_signature(request)
20
+ request.body.rewind
21
+ body = request.body.read
22
+ base_string = "#{request.env['HTTP_X_FRONT_REQUEST_TIMESTAMP']}:#{body}"
23
+ calculated_signature = OpenSSL::HMAC.base64digest(OpenSSL::Digest.new("sha256"), self.api_secret, base_string)
24
+ return calculated_signature == request.env["HTTP_X_FRONT_SIGNATURE"]
25
+ end
26
+
27
+ def self.webhook_response(request)
28
+ return Webhookdb::WebhookResponse.error("missing signature") unless request.env["HTTP_X_FRONT_SIGNATURE"]
29
+
30
+ from_front = Webhookdb::Front.verify_signature(request)
31
+ return Webhookdb::WebhookResponse.ok(status: 200) if from_front
32
+ return Webhookdb::WebhookResponse.error("invalid signature")
33
+ end
34
+
35
+ def self.initial_verification_request_response(request)
36
+ from_front = self.verify_signature(request)
37
+ if from_front
38
+ return Webhookdb::WebhookResponse.ok(
39
+ json: {challenge: request.env["HTTP_X_FRONT_CHALLENGE"]},
40
+ status: 200,
41
+ )
42
+ end
43
+ return Webhookdb::WebhookResponse.error("invalid credentials")
44
+ end
45
+
46
+ def self.auth_headers(token)
47
+ return {"Authorization" => "Bearer #{token}"}
48
+ end
49
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/security_utils"
4
+
5
+ class Webhookdb::Github
6
+ include Appydays::Configurable
7
+
8
+ configurable(:github) do
9
+ setting :http_timeout, 30
10
+ setting :activity_cron_expression, "*/5 * * * *"
11
+ end
12
+
13
+ def self.parse_link_header(header)
14
+ return Webhookdb::Shopify.parse_link_header(header)
15
+ end
16
+
17
+ # see https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries
18
+ def self.verify_webhook(body, hmac_header, webhook_secret)
19
+ calculated_hash = "sha256=" + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), webhook_secret, body)
20
+ return ActiveSupport::SecurityUtils.secure_compare(calculated_hash, hmac_header)
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+
5
+ module Webhookdb::GoogleCalendar
6
+ include Appydays::Configurable
7
+
8
+ configurable(:google_calendar) do
9
+ # How many calendars/events should we fetch in a single page?
10
+ # Higher uses slightly more memory but fewer API calls.
11
+ # Max of 2500.
12
+ setting :list_page_size, 2000
13
+ # How many rows should we upsert at a time?
14
+ # Higher is fewer upserts, but can create very large SQL strings,
15
+ # which can have negative performance.
16
+ setting :upsert_page_size, 500
17
+ # How long should watch channels live.
18
+ # Generally use Google's default (one week),
19
+ # but set shorter when testing.
20
+ setting :watch_ttl, 604_800
21
+ setting :http_timeout, 30
22
+ end
23
+
24
+ # Manual backfilling is not supported on Google Calendar integrations.
25
+ # If a manual backfill is attempted, direct customer to this url.
26
+ DOCUMENTATION_URL = "https://docs.webhookdb.com/guides/google-calendar/"
27
+
28
+ PUSH_NOT_SUPPORTED_SENTINEL_WATCH_ID = "ech-push-not-supported-for-requested-resource"
29
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "platform-api"
4
+
5
+ require "webhookdb"
6
+
7
+ class Webhookdb::Heroku
8
+ include Appydays::Configurable
9
+
10
+ configurable(:heroku) do
11
+ setting :oauth_id, "", key: "WEBHOOKDB_HEROKU_OAUTH_ID"
12
+ setting :oauth_token, "", key: "WEBHOOKDB_HEROKU_OAUTH_TOKEN"
13
+ setting :app_name, "", key: "HEROKU_APP_NAME"
14
+ end
15
+
16
+ def self.client
17
+ raise "No heroku:oauth_token configured" if self.oauth_token.blank?
18
+ @client ||= PlatformAPI.connect_oauth(self.oauth_token)
19
+ return @client
20
+ end
21
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "httparty"
4
+
5
+ require "appydays/loggable/httparty_formatter"
6
+
7
+ module Webhookdb::Http
8
+ # Error raised when some API has rate limited us.
9
+ class BaseError < StandardError; end
10
+
11
+ class Error < BaseError
12
+ attr_reader :response, :body, :uri, :status, :http_method
13
+
14
+ def initialize(response, msg=nil)
15
+ @response = response
16
+ @body = response.body
17
+ @headers = response.headers.to_h
18
+ @status = response.code
19
+ @uri = response.request.last_uri.dup
20
+ if @uri.query.present?
21
+ cleaned_params = CGI.parse(@uri.query).map { |k, v| k.include?("secret") ? [k, ".snip."] : [k, v] }
22
+ @uri.query = HTTParty::Request::NON_RAILS_QUERY_STRING_NORMALIZER.call(cleaned_params)
23
+ end
24
+ @http_method = response.request.http_method::METHOD
25
+ super(msg || self.to_s)
26
+ end
27
+
28
+ def to_s
29
+ return "HttpError(status: #{self.status}, method: #{self.http_method}, uri: #{self.uri}, body: #{self.body})"
30
+ end
31
+
32
+ alias inspect to_s
33
+ end
34
+
35
+ def self.user_agent
36
+ return Webhookdb.http_user_agent unless Webhookdb.http_user_agent.blank?
37
+ return "WebhookDB/#{Webhookdb::RELEASE} https://webhookdb.com #{Webhookdb::RELEASE_CREATED_AT}"
38
+ end
39
+
40
+ def self.extract_url_auth(url)
41
+ parsed_uri = URI(url)
42
+ if parsed_uri.userinfo.present?
43
+ auth_params = {
44
+ username: URI.decode_www_form_component(parsed_uri.user || ""),
45
+ password: URI.decode_www_form_component(parsed_uri.password || ""),
46
+ }
47
+ parsed_uri.user = parsed_uri.password = nil
48
+ cleaned_url = parsed_uri.to_s
49
+ return cleaned_url, auth_params
50
+ end
51
+ return url, nil
52
+ end
53
+
54
+ def self.check!(response, **options)
55
+ # All oks are ok
56
+ return if response.code < 300
57
+ # We expect 300s if we aren't following redirects
58
+ return if response.code < 400 && !options[:follow_redirects]
59
+ # Raise for 400s, or 300s if we were meant to follow redirects
60
+ raise Error, response
61
+ end
62
+
63
+ def self.get(url, query={}, **options, &)
64
+ self._setup_required_args(options)
65
+ opts = {query:, headers: {}}.merge(**options)
66
+ opts[:headers]["User-Agent"] = self.user_agent
67
+ # See https://github.com/jnunemaker/httparty/issues/784#issuecomment-1585714745
68
+ # I *think* this should be safe to always use.
69
+ opts[:headers]["Connection"] ||= "keep-alive"
70
+ r = HTTParty.get(url, **opts, &)
71
+ self.check!(r, **opts)
72
+ return r
73
+ end
74
+
75
+ def self.post(url, body={}, headers: {}, method: nil, check: true, **options, &)
76
+ self._setup_required_args(options)
77
+ headers["Content-Type"] ||= "application/json"
78
+ headers["User-Agent"] = self.user_agent
79
+ body = body.to_json if !body.is_a?(String) && headers["Content-Type"].include?("json")
80
+ opts = {body:, headers:}.merge(**options)
81
+ r = HTTParty.send(method || :post, url, **opts, &)
82
+ self.check!(r, **options) if check
83
+ return r
84
+ end
85
+
86
+ def self._setup_required_args(options)
87
+ raise ArgumentError, "must pass :timeout keyword" unless options.key?(:timeout)
88
+
89
+ raise ArgumentError, "must pass :logger keyword" unless options.key?(:logger)
90
+ options[:log_format] = :appydays
91
+ end
92
+
93
+ # Convenience wrapper around Down that handles gzip.
94
+ # @return Array<Down::ChunkedIO, IO> Tuple
95
+ def self.chunked_download(request_url, rewindable: false, **down_kw)
96
+ io = Down::NetHttp.open(request_url, rewindable:, **down_kw)
97
+ if io.data[:headers].fetch("Content-Encoding", "").include?("gzip")
98
+ # If the response is gzipped, Down doesn't handle it properly.
99
+ # Wrap it with gzip reader, and force the encoding to binary
100
+ # the server may send back a header like Content-Type: text/plain; UTF-8,
101
+ # so each line Down yields via #gets will have force_encoding('utf-8').
102
+ # https://github.com/janko/down/issues/87
103
+ io.instance_variable_set(:@encoding, "binary")
104
+ io = Zlib::GzipReader.wrap(io)
105
+ end
106
+ return io
107
+ end
108
+
109
+ def self.gzipped?(string)
110
+ return false if string.length < 3
111
+ b = string[..2].bytes
112
+ return b[0] == 0x1f && b[1] == 0x8b
113
+ end
114
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+
5
+ module Webhookdb::Icalendar
6
+ # Manual backfilling is not supported on iCalendar integrations.
7
+ # If a manual backfill is attempted, direct customer to this url.
8
+ DOCUMENTATION_URL = "https://docs.webhookdb.com/guides/icalendar/"
9
+
10
+ include Appydays::Configurable
11
+
12
+ configurable(:icalendar) do
13
+ # Do not store events older then this when syncing recurring events.
14
+ # Many icalendar feeds are misconfigured and this prevents enumerating 2000+ years of recurrence.
15
+ setting :oldest_recurring_event, "1990-01-01", convert: ->(s) { Date.parse(s) }
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb"
4
+
5
+ module Webhookdb::Id
6
+ ID_BYTES = 16
7
+
8
+ def self.new_opaque_id(prefix)
9
+ b36 = self.rand_enc(ID_BYTES)
10
+ return "#{prefix}_#{b36}"
11
+ end
12
+
13
+ def self.rand_enc(blen)
14
+ b = SecureRandom.bytes(blen)
15
+ return Digest.hexencode(b).to_i(16).to_s(36)
16
+ end
17
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/postgres/model"
4
+
5
+ # Support idempotent operations.
6
+ # This is very useful when
7
+ # 1) protecting the API against requests dispatched multiple times,
8
+ # as browsers are liable to do, and
9
+ # 2) designing parts of a system so they can be used idempotently, especially async jobs.
10
+ # This ensures an event can be republished if a job fails, but jobs that worked won't be re-run.
11
+ #
12
+ # In general, you do not use Idempotency instances directly;
13
+ # instead, you will use once_ever and every.
14
+ # For example, to only send a welcome email once:
15
+ #
16
+ # Webhookdb::Idempotency.once_ever.under_key("welcome-email-#{customer.id}") { send_welcome_email(customer) }
17
+ #
18
+ # Similarly, to prevent an action email from going out multiple times in a short period accidentally:
19
+ #
20
+ # Webhookdb::Idempotency.every(1.hour).under_key("new-order-#{order.id}") { send_new_order_email(order) }
21
+ #
22
+ # Note that idempotency cannot be executed while already in a transaction.
23
+ # If it were, the unique row would not be visible to other transactions.
24
+ # So the new row must be committed, then the idempotency evaluated (and the callback potentially run).
25
+ # To disable this check, set 'Postgres.unsafe_skip_transaction_check' to true,
26
+ # usually using the :no_transaction_check spec metadata.
27
+ #
28
+ class Webhookdb::Idempotency < Webhookdb::Postgres::Model(:idempotencies)
29
+ extend Webhookdb::MethodUtilities
30
+
31
+ NOOP = :skipped
32
+
33
+ # Skip the transaction check. Useful in unit tests. See class docs for details.
34
+ singleton_predicate_accessor :skip_transaction_check
35
+
36
+ def self.once_ever
37
+ idem = self.new
38
+ idem.__once_ever = true
39
+ return idem
40
+ end
41
+
42
+ def self.every(interval)
43
+ idem = self.new
44
+ idem.__every = interval
45
+ return idem
46
+ end
47
+
48
+ attr_accessor :__every, :__once_ever
49
+
50
+ def under_key(key, &block)
51
+ self.key = key
52
+ return self.execute(&block) if block
53
+ return self
54
+ end
55
+
56
+ def execute
57
+ Webhookdb::Postgres.check_transaction(
58
+ self.db,
59
+ "Cannot use idempotency while already in a transaction, since side effects may not be idempotent",
60
+ )
61
+
62
+ self.class.dataset.insert_conflict.insert(key: self.key)
63
+ self.db.transaction do
64
+ idem = Webhookdb::Idempotency[key: self.key].lock!
65
+ if idem.last_run.nil?
66
+ result = yield()
67
+ idem.update(last_run: Time.now)
68
+ return result
69
+ end
70
+ return NOOP if self.__once_ever
71
+ return NOOP if Time.now < (idem.last_run + self.__every)
72
+ result = yield()
73
+ idem.update(last_run: Time.now)
74
+ return result
75
+ end
76
+ end
77
+ end
78
+
79
+ # Table: idempotencies
80
+ # -------------------------------------------------------------------------------------
81
+ # Columns:
82
+ # id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
83
+ # created_at | timestamp with time zone | NOT NULL DEFAULT now()
84
+ # updated_at | timestamp with time zone |
85
+ # last_run | timestamp with time zone |
86
+ # key | text |
87
+ # Indexes:
88
+ # idempotencies_pkey | PRIMARY KEY btree (id)
89
+ # idempotencies_key_key | UNIQUE btree (key)
90
+ # -------------------------------------------------------------------------------------
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Webhookdb::Increase
4
+ extend Webhookdb::MethodUtilities
5
+ include Appydays::Configurable
6
+ include Appydays::Loggable
7
+
8
+ configurable(:increase) do
9
+ setting :http_timeout, 30
10
+ end
11
+
12
+ def self.webhook_response(request, webhook_secret)
13
+ http_signature = request.env["HTTP_X_BANK_WEBHOOK_SIGNATURE"]
14
+
15
+ return Webhookdb::WebhookResponse.error("missing hmac") if http_signature.nil?
16
+
17
+ request.body.rewind
18
+ request_data = request.body.read
19
+
20
+ computed_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha256"), webhook_secret, request_data)
21
+
22
+ if http_signature != "sha256=" + computed_signature
23
+ # Invalid signature
24
+ self.logger.warn "increase signature verification error"
25
+ return Webhookdb::WebhookResponse.error("invalid hmac")
26
+ end
27
+
28
+ return Webhookdb::WebhookResponse.ok
29
+ end
30
+
31
+ # this helper function finds the relevant object data and helps us avoid repeated code
32
+ def self.find_desired_object_data(body)
33
+ return body.fetch("data", body)
34
+ end
35
+
36
+ # this function interprets webhook contents to assist with filtering webhooks by object type in our increase services
37
+ def self.contains_desired_object(webhook_body, desired_object_name)
38
+ object_of_interest = self.find_desired_object_data(webhook_body)
39
+ object_id = object_of_interest["id"]
40
+ return object_id.include?(desired_object_name)
41
+ end
42
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+
5
+ module Webhookdb::Intercom
6
+ include Appydays::Configurable
7
+
8
+ configurable(:intercom) do
9
+ setting :client_id, "whdb_intercom_client_id", key: "INTERCOM_CLIENT_ID"
10
+ setting :client_secret, "whdb_intercom_client_secret", key: "INTERCOM_CLIENT_SECRET"
11
+ setting :http_timeout, 30
12
+ setting :page_size, 20
13
+ end
14
+
15
+ def self.verify_webhook(data, hmac_header)
16
+ calculated_hmac = "sha1=#{OpenSSL::HMAC.hexdigest('SHA1', self.client_secret, data)}"
17
+ return ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, hmac_header)
18
+ end
19
+
20
+ def self.auth_headers(token)
21
+ return {"Intercom-Version" => "2.9", "Authorization" => "Bearer #{token}"}
22
+ end
23
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "amigo/retry"
4
+ require "amigo/queue_backoff_job"
5
+ require "amigo/durable_job"
6
+ require "sidekiq"
7
+
8
+ # Use this to verify the behavior of durable jobs:
9
+ #
10
+ # - Ensure DISABLE_DURABLE_JOBS_POLL env var is set.
11
+ # - `make run` and then `Webhookdb::Async.open_web` to go to Sidekiq web UI.
12
+ # - `make run-workers`, and copy the PID.
13
+ # - From pry: `Webhookdb::Jobs::DurableSleeper.setup_test_run(30)`
14
+ # - Jobs are put into the queue and the workers will be 'running' (sleeping).
15
+ # - Wait for some jobs to be done sleeping.
16
+ # - `pkill -9 <pid>`, kills Sidekiq without cleanup.
17
+ # - `Webhookdb::Jobs::DurableSleeper.print_status` will print
18
+ # the queue size. It would be '10' if you started with 30 jobs,
19
+ # 10 processed, and the worker was killed while 10 more were processing
20
+ # (leaving 10 unprocessed jobs in the queue).
21
+ # It will also print dead jobs, which will be 0.
22
+ # It will also print processed jobs, will be be 10.
23
+ # - Restart workers with `make run-workers`.
24
+ # - The next 10 workers will run (queue). `print_status` returns 0 for queue and dead size,
25
+ # and 20 for jobs processed.
26
+ # - Run `Amigo::DurableJob.poll_jobs`.
27
+ # - Go to the web UI's Dead jobs. Observe 10 jobs are there. `print_status` also shows 10 jobs as dead.
28
+ # - Retry those jobs.
29
+ # - `print_status` shows 0 dead and 30 processed jobs.
30
+ #
31
+ class Webhookdb::Jobs::DurableSleeper
32
+ include Sidekiq::Job
33
+ include Amigo::DurableJob
34
+
35
+ MUX = Mutex.new
36
+ COUNTER_FILE = ".durable-sleeper-counter"
37
+
38
+ def self.heartbeat_extension
39
+ return 20.seconds
40
+ end
41
+
42
+ def perform(duration=5)
43
+ self.logger.info("sleeping")
44
+ sleep(duration)
45
+ MUX.synchronize do
46
+ done = File.read(COUNTER_FILE).to_i
47
+ done += 1
48
+ File.write(COUNTER_FILE, done.to_s)
49
+ end
50
+ end
51
+
52
+ def self.setup_test_run(count=30)
53
+ File.write(COUNTER_FILE, "0")
54
+ count.times { Webhookdb::Jobs::DurableSleeper.perform_async }
55
+ end
56
+
57
+ def self.print_status
58
+ done = File.read(COUNTER_FILE).to_i
59
+ puts "Queue Size: #{Sidekiq::Queue.new.size}"
60
+ puts "Processed: #{done}"
61
+ puts "Dead Set: #{Sidekiq::DeadSet.new.size}"
62
+ end
63
+ end
64
+
65
+ # Use this and BackoffShouldBeRun to test the behavior of BackoffJob.
66
+ #
67
+ # First, fill up the 'netout' queue with a ton of these slow jobs:
68
+ # From pry: `Webhookdb::Async.require_jobs; 500.times { Webhookdb::Jobs::BackoffShouldBeRescheduled.perform_async }`
69
+ #
70
+ # Then, fill up the other queues with fast jobs:
71
+ # `1000.times { Webhookdb::Jobs::BackoffShouldRun.perform_async }`
72
+ #
73
+ # Then go to http://localhost:18001/sidekiq (user/pass) to check the latency.
74
+ # The netout queue should get slow,
75
+ # but the other queues should not build up much of a backlog.
76
+ class Webhookdb::Jobs::BackoffShouldBeRescheduled
77
+ include Sidekiq::Job
78
+ include Amigo::DurableJob # Uncomment to verify performance with durable jobs, which hit the DB.
79
+ include Amigo::QueueBackoffJob
80
+
81
+ sidekiq_options queue: "netout"
82
+
83
+ def perform(duration=3)
84
+ sleep(duration)
85
+ end
86
+ end
87
+
88
+ class Webhookdb::Jobs::BackoffShouldRun
89
+ include Sidekiq::Job
90
+
91
+ def perform(duration=0.1)
92
+ sleep(duration)
93
+ end
94
+ end
95
+
96
+ class Webhookdb::Jobs::RetryChecker
97
+ include Sidekiq::Job
98
+
99
+ def perform(action, interval, attempts)
100
+ case action
101
+ when "retry"
102
+ raise Amigo::Retry::Retry, interval
103
+ when "die"
104
+ raise Amigo::Retry::Die
105
+ else
106
+ raise Amigo::Retry::Die.new(attempts, interval)
107
+ end
108
+ end
109
+ end
110
+
111
+ class Webhookdb::Jobs::Erroring
112
+ include Sidekiq::Job
113
+
114
+ def perform(succeed: false)
115
+ return if succeed
116
+ raise "erroring as asked!"
117
+ end
118
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "amigo/queue_backoff_job"
4
+ require "amigo/durable_job"
5
+ require "webhookdb/async/job"
6
+ require "webhookdb/jobs"
7
+
8
+ class Webhookdb::Jobs::Backfill
9
+ extend Webhookdb::Async::Job
10
+ include Amigo::DurableJob
11
+ include Amigo::QueueBackoffJob
12
+
13
+ on "webhookdb.backfilljob.run"
14
+ sidekiq_options queue: "netout"
15
+
16
+ def dependent_queues
17
+ # This is really the lowest-priority job so always defer to other queues.
18
+ return super
19
+ end
20
+
21
+ def _perform(event)
22
+ bfjob = self.lookup_model(Webhookdb::BackfillJob, event.payload)
23
+ sint = bfjob.service_integration
24
+ self.with_log_tags(sint.log_tags.merge(backfill_job_id: bfjob.opaque_id)) do
25
+ if bfjob.finished?
26
+ self.logger.info "skipping_finished_backfill_job"
27
+ else
28
+ sint.replicator.backfill(bfjob)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/async/job"
4
+
5
+ class Webhookdb::Jobs::CreateMirrorTable
6
+ extend Webhookdb::Async::Job
7
+
8
+ on "webhookdb.serviceintegration.created"
9
+ sidekiq_options queue: "critical"
10
+
11
+ def _perform(event)
12
+ sint = self.lookup_model(Webhookdb::ServiceIntegration, event)
13
+ self.with_log_tags(sint.log_tags) do
14
+ svc = Webhookdb::Replicator.create(sint)
15
+ svc.create_table(if_not_exists: true)
16
+ end
17
+ end
18
+ end