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,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake/tasklib"
4
+
5
+ require "webhookdb"
6
+ require "webhookdb/postgres"
7
+
8
+ module Webhookdb::Tasks
9
+ class Fixture < Rake::TaskLib
10
+ def initialize
11
+ super()
12
+ namespace :fixture do
13
+ desc "Create a bunch of fake integrations and fill them with data."
14
+ task :full do
15
+ require "webhookdb"
16
+ Webhookdb.load_app
17
+ require "webhookdb/fixtures"
18
+ Webhookdb::Fixtures.load_all
19
+ org = Webhookdb::Fixtures.organization.create
20
+ org.prepare_database_connections?
21
+ sints = Array.new(3) { Webhookdb::Fixtures.service_integration(organization: org).create }
22
+ now = Time.now
23
+ sints.each do |sint|
24
+ svc = sint.replicator
25
+ svc.create_table
26
+ Array.new(5000) do |i|
27
+ svc.upsert_webhook_body({"my_id" => i.to_s, "at" => (now - i.seconds).iso8601})
28
+ end
29
+ end
30
+ puts "Created Webhookdb::Organization[#{org.id}]"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake/tasklib"
4
+
5
+ module Webhookdb::Tasks
6
+ class Message < Rake::TaskLib
7
+ def initialize
8
+ super()
9
+ namespace :message do
10
+ desc "Render the specified message"
11
+ task :render, [:template_class, :out] do |_t, args|
12
+ template_class_name = args[:template_class] or
13
+ raise "Provide the template class name (NewCustomer or new_customer) as the first argument"
14
+ outpath = args[:out]
15
+ outpath = nil if outpath.blank? || outpath == "-"
16
+ if outpath
17
+ html_io = File.open(outpath, "w")
18
+ feedback_io = $stdout
19
+ else
20
+ html_io = $stdout
21
+ feedback_io = $stderr
22
+ end
23
+
24
+ ENV["WEBHOOKDB_DB_SLOW_QUERY_SECONDS"] ||= "1"
25
+ require "webhookdb"
26
+ Webhookdb.load_app
27
+ SemanticLogger.appenders.to_a.each { |a| SemanticLogger.remove_appender(a) }
28
+ SemanticLogger.add_appender(io: feedback_io)
29
+
30
+ commit = Webhookdb::RACK_ENV != "test"
31
+ delivery = Webhookdb::Message::Delivery.preview(template_class_name.classify, commit:)
32
+ feedback_io << "*** Created MessageDelivery: #{delivery.values}\n\n"
33
+ feedback_io << delivery.body_with_mediatype!("text/plain")&.content
34
+ feedback_io << "\n\n"
35
+ if outpath
36
+ feedback_io << "*** Writing HTML output to #{outpath}\n"
37
+ elsif html_io.tty?
38
+ feedback_io << "*** Writing HTML output to stdout.\n"
39
+ feedback_io << "*** Redirect it to a file (> temp.html), pass OUT to write it to a file (OUT=temp.html),\n"
40
+ feedback_io << "*** or view it at /admin_api/v1/message_deliveries/last\n\n"
41
+ else
42
+ feedback_io << "*** Writing HTML output to redirected stdout.\n"
43
+ end
44
+ html_io << delivery.body_with_mediatype!("text/html")&.content
45
+ html_io << "\n"
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake/tasklib"
4
+ require "sequel"
5
+
6
+ require "webhookdb"
7
+ require "webhookdb/postgres"
8
+
9
+ module Webhookdb::Tasks
10
+ class Regress < Rake::TaskLib
11
+ def initialize
12
+ super()
13
+ namespace :regress do
14
+ desc "Creates databases for all orgs that do not have them."
15
+ task :prepare do
16
+ Webhookdb.load_app
17
+ Webhookdb::Organization.where(readonly_connection_url_raw: nil).each do |org|
18
+ org.prepare_database_connections
19
+ org.migrate_replication_tables
20
+ end
21
+ end
22
+
23
+ desc "Prints out all service integrations that have untrimmed logged webhooks."
24
+ task :list_available do
25
+ Webhookdb.load_app
26
+ opaque_ids = Webhookdb::LoggedWebhook.where(truncated_at: nil).
27
+ distinct(:service_integration_opaque_id).
28
+ select_map(:service_integration_opaque_id)
29
+ sints = Webhookdb::ServiceIntegration.where(opaque_id: opaque_ids).all
30
+ self.print_service_integrations(sints)
31
+ end
32
+
33
+ desc "Replay the last :count webhooks going to the service integration with the given opaque id. " \
34
+ "Use -1 for all webhooks."
35
+ task :replay, [:opaque_id, :count] do |_, args|
36
+ opaque_id = args.fetch(:opaque_id)
37
+ count = args.fetch(:count).to_i
38
+ Webhookdb.load_app
39
+ ds = Webhookdb::LoggedWebhook.where(truncated_at: nil).
40
+ where(service_integration_opaque_id: opaque_id).
41
+ order(Sequel.asc(:inserted_at))
42
+ ds = ds.limit(count) if count >= 0
43
+ ds.paged_each do |lw|
44
+ good = lw.retry_one
45
+ puts "#{lw.pk} failed" unless good
46
+ end
47
+ end
48
+
49
+ desc "Prints out all service integrations that have backfill info available."
50
+ task :list_backfill do
51
+ Webhookdb.load_app
52
+ sints = Webhookdb::ServiceIntegration.exclude(backfill_key: nil).all
53
+ self.print_service_integrations(sints)
54
+ end
55
+
56
+ desc "Runs a backfill for the service integration with the given opaque id. " \
57
+ "Regression backfills are limited to one page."
58
+ task :backfill, [:opaque_id] do |_, args|
59
+ opaque_id = args.fetch(:opaque_id)
60
+ Webhookdb.load_app
61
+ sint = Webhookdb::ServiceIntegration[opaque_id:] or raise "No service integration for #{opaque_id}"
62
+ bfjob = Webhookdb::BackfillJob.create_recursive(service_integration: sint, incremental: false)
63
+ sint.replicator.backfill(bfjob)
64
+ end
65
+ end
66
+ end
67
+
68
+ def print_service_integrations(iter)
69
+ rows = iter.map do |sint|
70
+ name = "#{sint.organization.name} (#{sint.organization_id})"
71
+ [name, sint.service_name, sint.opaque_id, sint.table_name]
72
+ end
73
+ rows.sort!
74
+ self.table_print rows
75
+ end
76
+
77
+ def table_print(rows)
78
+ max_lengths = rows.each_with_object({}) do |row, m|
79
+ row.each_with_index { |value, idx| m[idx] = [m.fetch(idx, 0), value.to_s.length].max }
80
+ end
81
+ row_strings = rows.map do |row|
82
+ row.each_with_index.map { |value, idx| "%-#{max_lengths[idx]}s" % value }.join("\t")
83
+ end
84
+ puts row_strings
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake/tasklib"
4
+
5
+ require "webhookdb"
6
+
7
+ module Webhookdb::Tasks
8
+ class Release < Rake::TaskLib
9
+ def initialize
10
+ super()
11
+ desc "Migrate replication tables for each integration, ensure all columns and backfill new columns."
12
+ task :migrate_replication_tables do
13
+ Webhookdb.load_app
14
+ Webhookdb::Organization.enqueue_migrate_all_replication_tables
15
+ end
16
+
17
+ desc "Run the release script against the current environment."
18
+ task :release do
19
+ Rake::Task["db:migrate"].invoke
20
+ Rake::Task["migrate_replication_tables"].invoke
21
+ if ENV["HEROKU_APP_ID"] && (ENV["RUN_INTEGRATION_TESTS_ON_RELEASE"] == "true")
22
+ Rake::Task["specs:heroku_integration_step1"].invoke
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake/tasklib"
4
+
5
+ require "webhookdb"
6
+
7
+ module Webhookdb::Tasks
8
+ class Sidekiq < Rake::TaskLib
9
+ def initialize
10
+ super()
11
+ namespace :sidekiq do
12
+ desc "Clear the Sidekiq redis DB (flushdb). " \
13
+ "Only use on local, and only for legit reasons, " \
14
+ "not to paper over problems that will show on staging and prod " \
15
+ "(like removing a job class)."
16
+ task :reset do
17
+ require "webhookdb/async"
18
+ ::Sidekiq.redis(&:flushdb)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake/tasklib"
4
+ require "stringio"
5
+
6
+ require "webhookdb"
7
+
8
+ module Webhookdb::Tasks
9
+ class Specs < Rake::TaskLib
10
+ def initialize
11
+ super()
12
+ namespace :specs do
13
+ desc "Run API integration tests"
14
+ task :integration do
15
+ require "rspec/core"
16
+ require "slack-notifier"
17
+ require "webhookdb/spec_helpers/integration"
18
+ require "webhookdb/spec_helpers/citest"
19
+ Webhookdb::SpecHelpers::Citest.run_tests("integration")
20
+ end
21
+
22
+ desc "The release process needs to finish quickly, so start the integration tests in another dyno."
23
+ task :heroku_integration_step1 do
24
+ require "webhookdb/heroku"
25
+ Webhookdb::Heroku.client.dyno.create(
26
+ Webhookdb::Heroku.app_name,
27
+ command: "bundle exec rake specs:heroku_integration_step2",
28
+ attach: false,
29
+ time_to_live: 1.minute.to_i,
30
+ type: "run",
31
+ )
32
+ end
33
+ desc "Sleep 20 seconds to wait for the **next** one-off dyno to have the new code."
34
+ task :heroku_integration_step2 do
35
+ sleep(20)
36
+ require "webhookdb/heroku"
37
+ Webhookdb::Heroku.client.dyno.create(
38
+ Webhookdb::Heroku.app_name,
39
+ command: "bundle exec rake specs:integration_step3",
40
+ env: {"INTEGRATION_TESTS" => "true"},
41
+ attach: false,
42
+ time_to_live: 10.minute.to_i,
43
+ type: "run",
44
+ )
45
+ end
46
+ desc "Run the actual integration tests."
47
+ task :heroku_integration_step3 do
48
+ require "webhookdb/heroku"
49
+ Rake::Task["specs:integration"].invoke
50
+ end
51
+
52
+ desc "Do a thing through Rake so it's easy to run under the debugger"
53
+ task :debugtask do
54
+ require "webhookdb"
55
+ Webhookdb.load_app
56
+ sint = Webhookdb::ServiceIntegration[84]
57
+ row_pk = 710
58
+ expiring_before = 1.week.from_now
59
+ sint.replicator.renew_watch_channel(row_pk:, expiring_before:)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+
5
+ module Webhookdb::Theranest
6
+ include Appydays::Configurable
7
+
8
+ configurable(:theranest) do
9
+ setting :cron_expression, "30 8 * * *" # default to midnight
10
+ setting :appointment_look_back_months, 12
11
+ setting :appointment_look_forward_months, 3
12
+ setting :page_size, 50
13
+ setting :http_timeout, 30
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+
5
+ module Webhookdb::Transistor
6
+ include Appydays::Configurable
7
+
8
+ configurable(:transistor) do
9
+ setting :episode_cron_expression, "30 * * * *"
10
+ setting :show_cron_expression, "0 0 */1 * *"
11
+ setting :http_timeout, 30
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+ require "appydays/loggable"
5
+
6
+ module Webhookdb::Twilio
7
+ include Appydays::Configurable
8
+ include Appydays::Loggable
9
+
10
+ configurable(:twilio) do
11
+ setting :http_timeout, 30
12
+ end
13
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Webhookdb::TypedStruct
4
+ def initialize(**kwargs)
5
+ self._apply(self._defaults.merge(kwargs))
6
+ end
7
+
8
+ def _defaults
9
+ return {}
10
+ end
11
+
12
+ def [](k)
13
+ return self.send(k)
14
+ end
15
+
16
+ # Modify the receiver with kwargs.
17
+ def _apply(kwargs)
18
+ kwargs.each do |k, v|
19
+ raise TypeError, "invalid struct field #{k}" unless self.respond_to?(k)
20
+ self.instance_variable_set(:"@#{k}", v)
21
+ end
22
+ end
23
+
24
+ def change(**)
25
+ c = self.dup
26
+ c._apply(**)
27
+ return c
28
+ end
29
+
30
+ def as_json
31
+ return self.instance_values.as_json
32
+ end
33
+
34
+ protected def typecheck!(field, type, nullable: false)
35
+ value = self.send(field)
36
+ return if nullable && value.nil?
37
+ if type == :boolean
38
+ return if [true, false].include?(value)
39
+ elsif value.is_a?(type)
40
+ return
41
+ end
42
+ raise ArgumentError, "#{field} #{value.inspect} must be a #{type.name}"
43
+ end
44
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Webhookdb
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/typed_struct"
4
+
5
+ class Webhookdb::WebhookResponse < Webhookdb::TypedStruct
6
+ # The standard WebhookDB Secret header, in RFC header format (My-Hdr).
7
+ SECRET_HEADER_RFC = "Whdb-Webhook-Secret"
8
+ # The standard WebhookDB secret header, in Rack header format (HTTP_MY_HDR).
9
+ SECRET_HEADER_RACK = "HTTP_WHDB_WEBHOOK_SECRET"
10
+
11
+ # Compare the value of the SECRET_HEADER_RACK in the request header.
12
+ # @param [Rack::Request] request
13
+ # @param [String] webhook_secret
14
+ # @return [Webhookdb::WebhookResponse]
15
+ def self.for_standard_secret(request, webhook_secret, ok_status: 202)
16
+ hdr_secret = request.env[SECRET_HEADER_RACK]
17
+ return self.error("missing secret header") if hdr_secret.nil?
18
+ matches = ActiveSupport::SecurityUtils.secure_compare(webhook_secret, hdr_secret)
19
+ return self.error("secret mismatch") unless matches
20
+ return self.ok(status: ok_status)
21
+ end
22
+
23
+ attr_reader :status,
24
+ :headers,
25
+ :body,
26
+ :reason
27
+
28
+ def self.error(reason, status: 401)
29
+ return self.new(status:, json: {message: reason}, reason:)
30
+ end
31
+
32
+ def self.ok(json: {o: "k"}, status: 202)
33
+ return self.new(status:, json:)
34
+ end
35
+
36
+ def initialize(status:, body: nil, json: nil, reason: nil, headers: {})
37
+ raise "Reason must be provided if returning an error" if !reason && status >= 400
38
+ if json
39
+ body = json.to_json
40
+ headers["Content-Type"] = "application/json"
41
+ end
42
+ raise ":body or :json must be provided" if body.nil?
43
+ super(status:, body:, headers:, reason:)
44
+ end
45
+
46
+ # @return [Array<Integer, Hash, String>]
47
+ def to_rack
48
+ return [self.status, self.headers, self.body]
49
+ end
50
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/postgres/model"
4
+
5
+ require "webhookdb/webhook_subscription"
6
+
7
+ # Represents the attempted delivery of a rowupsert to a particular webhook subscription.
8
+ # See WebhookSubscription for more details.
9
+ class Webhookdb::WebhookSubscription::Delivery < Webhookdb::Postgres::Model(:webhook_subscription_deliveries)
10
+ plugin :timestamps
11
+
12
+ many_to_one :webhook_subscription, class: "Webhookdb::WebhookSubscription"
13
+
14
+ # See WebhookSubhscription#attempt_delivery
15
+ def attempt_delivery
16
+ self.webhook_subscription.attempt_delivery(self)
17
+ end
18
+
19
+ # Add an attempt to this instance.
20
+ def add_attempt(status:, at: Time.now)
21
+ self.attempt_timestamps << at
22
+ self.modified!(:attempt_timestamps)
23
+ self.attempt_http_response_statuses << status
24
+ self.modified!(:attempt_http_response_statuses)
25
+ end
26
+
27
+ # Create a list of Attempt instances.
28
+ def attempts
29
+ return self.attempt_timestamps.
30
+ zip(self.attempt_http_response_statuses).
31
+ map { |(at, status)| Attempt.new(at, status) }
32
+ end
33
+
34
+ # Fast path for getting the total attempt count.
35
+ def attempt_count
36
+ return self.attempt_timestamps.length
37
+ end
38
+
39
+ # Return the latest attempt, or nil if there have been no attempts.
40
+ def latest_attempt
41
+ cnt = self.attempt_count
42
+ return nil if cnt.zero?
43
+ ts = self.attempt_timestamps[cnt - 1]
44
+ status = self.attempt_http_response_statuses[cnt - 1]
45
+ return Attempt.new(ts, status)
46
+ end
47
+
48
+ # One of 'pending' (no attempts), 'success', or 'error'.
49
+ def latest_attempt_status
50
+ att = self.latest_attempt
51
+ return "pending" if att.nil?
52
+ return att.success ? "success" : "error"
53
+ end
54
+
55
+ class Attempt
56
+ attr_reader :at, :status, :success
57
+
58
+ def initialize(at, status)
59
+ @at = at
60
+ @status = status
61
+ @success = status < 300
62
+ end
63
+ end
64
+ end
65
+
66
+ # Table: webhook_subscription_deliveries
67
+ # ------------------------------------------------------------------------------------------------------------------------------
68
+ # Columns:
69
+ # id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
70
+ # created_at | timestamp with time zone | NOT NULL DEFAULT now()
71
+ # attempt_timestamps | timestamp with time zone[] | NOT NULL DEFAULT ARRAY[]::timestamp with time zone[]
72
+ # attempt_http_response_statuses | smallint[] | NOT NULL DEFAULT ARRAY[]::smallint[]
73
+ # payload | jsonb | NOT NULL
74
+ # webhook_subscription_id | integer | NOT NULL
75
+ # Indexes:
76
+ # webhook_subscription_deliveries_pkey | PRIMARY KEY btree (id)
77
+ # webhook_subscription_deliveries_webhook_subscription_id_index | btree (webhook_subscription_id)
78
+ # Check constraints:
79
+ # balanced_attempts | (array_length(attempt_timestamps, 1) = array_length(attempt_http_response_statuses, 1))
80
+ # Foreign key constraints:
81
+ # webhook_subscription_deliveries_webhook_subscription_id_fkey | (webhook_subscription_id) REFERENCES webhook_subscriptions(id)
82
+ # ------------------------------------------------------------------------------------------------------------------------------