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,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/postgres"
4
+
5
+ module Webhookdb::Postgres::Validations
6
+ # Ensures that only one of the passed columns is not nil.
7
+ def validates_mutually_exclusive(*cols, predicate: :nil?)
8
+ truthy_cols = cols.find_all { |col| !self[col].send(predicate) }
9
+ multiple_set = truthy_cols.length > 1
10
+ return unless multiple_set
11
+ self.errors.add(truthy_cols.first, "is mutually exclusive with other set columns #{truthy_cols[1..].join(', ')}")
12
+ end
13
+
14
+ # Ensures that either all of the passed columns are nil or none of them are.
15
+ def validates_all_or_none(*cols, predicate: :nil?)
16
+ truthy_cols = cols.find_all { |col| !self[col].send(predicate) }
17
+
18
+ return if truthy_cols.empty? || truthy_cols.length == cols.length
19
+
20
+ msg = "the columns #{cols[1..].join(', ')} must all be set or all be #{predicate.to_s.delete_suffix('?')}"
21
+ self.errors.add(cols.first, msg)
22
+ end
23
+
24
+ # Ensures that at least one of the passed columns is not nil.
25
+ def validates_at_least_one_of(*cols, predicate: :nil?)
26
+ any_truthy = cols.any? { |col| !self[col].send(predicate) }
27
+ return if any_truthy
28
+ msg = "at least one of #{cols.join(', ')} must be not #{predicate.to_s.delete_suffix('?')}"
29
+ self.errors.add(cols.first, msg)
30
+ end
31
+
32
+ # Ensures that one and only one of the passed columns is not nil.
33
+ def validates_exactly_one_of(*cols, predicate: :nil?)
34
+ self.validates_at_least_one_of(*cols, predicate:)
35
+ self.validates_mutually_exclusive(*cols, predicate:)
36
+ end
37
+
38
+ def validates_ip_address(col)
39
+ return if self[col].respond_to?(:ipv4?)
40
+ begin
41
+ IPAddr.new(self[col])
42
+ rescue IPAddr::Error
43
+ self.errors.add(col, "is not a valid INET address")
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pg"
4
+ require "appydays/loggable"
5
+ require "sequel"
6
+ require "sequel/core"
7
+ require "sequel/adapters/postgres"
8
+
9
+ require "appydays/loggable/sequel_logger"
10
+ require "webhookdb"
11
+
12
+ module Webhookdb::Postgres
13
+ extend Webhookdb::MethodUtilities
14
+ include Appydays::Loggable
15
+
16
+ class InTransaction < StandardError; end
17
+
18
+ singleton_attr_accessor :unsafe_skip_transaction_check
19
+ @unsafe_skip_transaction_check = false
20
+ def self.check_transaction(db, error_msg)
21
+ return true if self.unsafe_skip_transaction_check
22
+ return true unless db.in_transaction?
23
+ raise InTransaction, error_msg
24
+ end
25
+
26
+ # Sequel API -- load some global extensions and plugins
27
+ Sequel.extension(
28
+ :core_extensions,
29
+ :core_refinements,
30
+ :is_distinct_from,
31
+ :pg_array,
32
+ :pg_array_ops,
33
+ :pg_inet,
34
+ :pg_inet_ops,
35
+ :pg_json,
36
+ :pg_json_ops,
37
+ :pg_range,
38
+ :pg_range_ops,
39
+ :symbol_as_refinement,
40
+ )
41
+ Sequel::Model.plugin(:force_encoding, "UTF-8")
42
+
43
+ # Require paths for model superclasses.
44
+ SUPERCLASSES = [
45
+ "webhookdb/postgres/model",
46
+ ].freeze
47
+
48
+ # Require paths for all Sequel models used by the app.
49
+ MODELS = [
50
+ "webhookdb/backfill_job",
51
+ "webhookdb/customer",
52
+ "webhookdb/customer/reset_code",
53
+ "webhookdb/database_document",
54
+ "webhookdb/idempotency",
55
+ "webhookdb/logged_webhook",
56
+ "webhookdb/message/body",
57
+ "webhookdb/message/delivery",
58
+ "webhookdb/oauth/session",
59
+ "webhookdb/organization",
60
+ "webhookdb/organization/database_migration",
61
+ "webhookdb/organization_membership",
62
+ "webhookdb/role",
63
+ "webhookdb/service_integration",
64
+ "webhookdb/subscription",
65
+ "webhookdb/sync_target",
66
+ "webhookdb/webhook_subscription",
67
+ "webhookdb/webhook_subscription/delivery",
68
+ ].freeze
69
+
70
+ # If true, deferred model events publish immediately.
71
+ # Should only be true for some types of testing.
72
+ singleton_predicate_accessor :do_not_defer_events
73
+
74
+ # The list of model superclasses (that get their own database connections)
75
+ singleton_attr_reader :model_superclasses
76
+ @model_superclasses = Set.new
77
+
78
+ ### Register the given +superclass+ as a base class for a set of models, for operations
79
+ ### which should happen on all the current database connections.
80
+ def self.register_model_superclass(superclass)
81
+ self.logger.debug "Registered model superclass: %p" % [superclass]
82
+ self.model_superclasses << superclass
83
+ end
84
+
85
+ ##
86
+ # The list of models that will be required once the database connection has been established.
87
+ singleton_attr_reader :registered_models
88
+ @registered_models = Set.new
89
+
90
+ ### Add a +path+ to require once the database connection is set.
91
+ def self.register_model(path)
92
+ self.logger.debug "Registered model for requiring: %s" % [path]
93
+
94
+ # If the connection's set, require the path immediately.
95
+ if self.model_superclasses.any?(&:db)
96
+ Appydays::Loggable[self].silence(:fatal) do
97
+ require(path)
98
+ end
99
+ end
100
+
101
+ self.registered_models << path
102
+ end
103
+
104
+ ### Require the model classes once the database connection has been established
105
+ def self.require_models
106
+ self.registered_models.each do |path|
107
+ require path
108
+ end
109
+ end
110
+
111
+ ### Call the block for each registered model superclass.
112
+ def self.each_model_superclass(&)
113
+ self.model_superclasses.each(&)
114
+ end
115
+
116
+ def self.each_model_class(&)
117
+ self.each_model_superclass do |sc|
118
+ sc.descendants.each(&)
119
+ end
120
+ end
121
+
122
+ def self.run_all_migrations(target: nil)
123
+ Sequel.extension :migration
124
+ Webhookdb::Postgres.each_model_superclass do |cls|
125
+ cls.install_all_extensions
126
+ Sequel::Migrator.run(cls.db, Pathname(__FILE__).dirname.parent.parent + "db/migrations", target:)
127
+ end
128
+ end
129
+
130
+ # We can always register the models right away, since it does not have a side effect.
131
+ MODELS.each do |m|
132
+ self.register_model(m)
133
+ end
134
+
135
+ # After configuration, load superclasses. You may need these without loading models,
136
+ # like if you need access to their DBs without loading them
137
+ # (if their tables do not yet exist)
138
+ def self.load_superclasses
139
+ SUPERCLASSES.each do |sc|
140
+ require(sc)
141
+ end
142
+ end
143
+
144
+ # After configuration, require in the model superclass files,
145
+ # to make sure their .db gets set and they're in model_superclasses.
146
+ def self.load_models
147
+ self.load_superclasses
148
+ Appydays::Loggable[self].silence(:fatal) do
149
+ self.require_models
150
+ end
151
+ end
152
+
153
+ # Return 'Time.now' as an expression suitable for Sequel/SQL.
154
+ # In some cases (like range @> expressions) you need to cast to a timestamptz explicitly,
155
+ # the implicit cast isn't enough.
156
+ # And because 'Time.now' is an external dependency, we should always use Sequel.delay,
157
+ # to avoid any internal caching it will do,
158
+ # like in association blocks: https://github.com/jeremyevans/sequel/blob/master/doc/association_basics.rdoc#block-
159
+ def self.now_sql(&block)
160
+ block ||= -> { Time.now }
161
+ return Sequel.delay { Sequel.cast(block.call, :timestamptz) }
162
+ end
163
+
164
+ # Call block immediately if not deferring events; otherwise call it after db commit.
165
+ def self.defer_after_commit(db, &block)
166
+ raise LocalJumpError unless block
167
+ return yield if self.do_not_defer_events?
168
+ return db.after_commit(&block)
169
+ end
170
+
171
+ def self.defer_after_rollback(db, &block)
172
+ raise LocalJumpError unless block
173
+ return yield if self.do_not_defer_events?
174
+ return db.after_rollback(&block)
175
+ end
176
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+
5
+ module Webhookdb::Postmark
6
+ include Appydays::Configurable
7
+
8
+ configurable(:postmark) do
9
+ # See https://postmarkapp.com/support/article/800-ips-for-firewalls#webhooks
10
+ setting :allowed_ips,
11
+ ["127.0.0.1", "3.134.147.250", "50.31.156.6", "50.31.156.77", "18.217.206.57"],
12
+ convert: ->(s) { s.split.map(&:strip) }
13
+ end
14
+
15
+ def self.webhook_response(request)
16
+ ip = request.ip
17
+ allowed = self.allowed_ips.include?(ip)
18
+ return allowed ? Webhookdb::WebhookResponse.ok : Webhookdb::WebhookResponse.error("invalid ip")
19
+ end
20
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+ require "redis_client"
5
+
6
+ module Webhookdb::Redis
7
+ include Appydays::Configurable
8
+
9
+ class << self
10
+ attr_accessor :cache
11
+ end
12
+
13
+ configurable(:redis) do
14
+ setting :cache_url, "redis://localhost:6379/0"
15
+ setting :cache_url_provider, "REDIS_URL"
16
+ setting :verify_ssl, false
17
+
18
+ after_configured do
19
+ url = ENV.fetch(self.cache_url_provider, self.cache_url)
20
+ cache_params = {url:, reconnect_attempts: 1}
21
+ cache_params[:ssl_params] = {verify_mode: OpenSSL::SSL::VERIFY_NONE} unless
22
+ self.verify_ssl
23
+ redis_config = RedisClient.config(**cache_params)
24
+ self.cache = redis_config.new_pool(
25
+ timeout: Webhookdb::Dbutil.pool_timeout,
26
+ size: Webhookdb::Dbutil.max_connections,
27
+ )
28
+ end
29
+ end
30
+
31
+ def self.cache_key(parts)
32
+ tail = parts.join("/")
33
+ return "cache/#{tail}"
34
+ end
35
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "down"
4
+
5
+ require "webhookdb/xml"
6
+
7
+ class Webhookdb::Replicator::AtomSingleFeedV1 < Webhookdb::Replicator::Base
8
+ include Appydays::Loggable
9
+
10
+ # @return [Webhookdb::Replicator::Descriptor]
11
+ def self.descriptor
12
+ return Webhookdb::Replicator::Descriptor.new(
13
+ name: "atom_single_feed_v1",
14
+ ctor: ->(sint) { Webhookdb::Replicator::AtomSingleFeedV1.new(sint) },
15
+ feature_roles: [],
16
+ resource_name_singular: "Atom Single Feed",
17
+ supports_backfill: true,
18
+ description: "Convert any Atom XML feed into a database table for querying and persistent archiving.",
19
+ api_docs_url: "https://en.wikipedia.org/wiki/Atom_(web_standard)",
20
+ )
21
+ end
22
+
23
+ def _remote_key_column
24
+ return Webhookdb::Replicator::Column.new(:entry_id, TEXT, data_key: "id")
25
+ end
26
+
27
+ CONV_GEO_LAT = Webhookdb::Replicator::Column.converter_array_element(index: 0, sep: " ", cls: DECIMAL)
28
+ CONV_GEO_LNG = Webhookdb::Replicator::Column.converter_array_element(index: 1, sep: " ", cls: DECIMAL)
29
+
30
+ def _denormalized_columns
31
+ col = Webhookdb::Replicator::Column
32
+ return [
33
+ col.new(:row_created_at, TIMESTAMP, index: true, optional: true, defaulter: :now),
34
+ col.new(:updated, TIMESTAMP, index: true),
35
+ col.new(:title, TEXT),
36
+ col.new(:published, TIMESTAMP, index: true, optional: true),
37
+ col.new(:geo_lat, DECIMAL, data_key: "georss:point", optional: true, converter: CONV_GEO_LAT),
38
+ col.new(:geo_lng, DECIMAL, data_key: "georss:point", optional: true, converter: CONV_GEO_LNG),
39
+ ]
40
+ end
41
+
42
+ def _timestamp_column_name
43
+ return :updated
44
+ end
45
+
46
+ def _resource_and_event(request)
47
+ return request.body, nil
48
+ end
49
+
50
+ def _update_where_expr
51
+ return self.qualified_table_sequel_identifier[:updated] < Sequel[:excluded][:updated]
52
+ end
53
+
54
+ def _upsert_update_expr(inserting, **_kwargs)
55
+ update = super
56
+ # Only set created_at if it's not set so the initial insert isn't modified.
57
+ self._coalesce_excluded_on_update(update, [:row_created_at])
58
+ return update
59
+ end
60
+
61
+ def _fetch_backfill_page(*)
62
+ io = Webhookdb::Http.get(self.service_integration.api_url, logger: self.logger, timeout: 30)
63
+ feed_obj = Webhookdb::Xml::Atom.parse(io.body)
64
+ return feed_obj.fetch("entries"), nil
65
+ end
66
+
67
+ def _verify_backfill_err_msg
68
+ return "Sorry, we can't reach that URL. Please double check it and try again."
69
+ end
70
+
71
+ def _backfillers
72
+ return [Backfiller.new(self)]
73
+ end
74
+
75
+ class Backfiller < Webhookdb::Replicator::Base::ServiceBackfiller
76
+ include Webhookdb::Backfiller::Bulk
77
+ attr_reader :upserting_replicator
78
+
79
+ def initialize(replicator)
80
+ super
81
+ @upserting_replicator = @svc
82
+ end
83
+
84
+ def upsert_page_size = 500
85
+ def conditional_upsert? = true
86
+ def prepare_body(body) = body
87
+ end
88
+
89
+ def _webhook_response(_request)
90
+ return Webhookdb::WebhookResponse.ok
91
+ end
92
+
93
+ def calculate_backfill_state_machine
94
+ step = Webhookdb::Replicator::StateMachineStep.new
95
+ # Must set this to fake out having credentials.
96
+ self.service_integration.update(backfill_key: "placeholder") if self.service_integration.backfill_key.blank?
97
+ if self.service_integration.api_url.blank?
98
+ step.output = %(You're about to sync entries from an Atom URL into WebhookDB.
99
+ This will create a row for each 'entry' in the given feed,
100
+ and insert/update new rows periodically.
101
+
102
+ Paste in the URL to sync, and press Enter.)
103
+ return step.prompting("URL").api_url(self.service_integration)
104
+ end
105
+ unless (result = self.verify_backfill_credentials).verified
106
+ self.service_integration.update(api_url: "")
107
+ step.output = result.message
108
+ return step.prompting("URL").api_url(self.service_integration)
109
+ end
110
+ step.output = %(
111
+ All set! Your feed will be synced momentarily and then every few hours after that.
112
+
113
+ #{self._query_help_output})
114
+ return step.completed
115
+ end
116
+ end