webhookdb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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