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,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/spec_helpers"
4
+
5
+ # Set :whdbisolation metadatabase. Use the isolation mode, or :reset to just reset config
6
+ # before and after the spec.
7
+ module Webhookdb::SpecHelpers::Whdb
8
+ def self.included(context)
9
+ context.before(:each) do |example|
10
+ if (isomode = example.metadata[:whdbisolation])
11
+ Webhookdb::Organization::DbBuilder.isolation_mode = isomode if isomode.is_a?(String)
12
+ Webhookdb::Organization::DbBuilder.reset_configuration if isomode == :reset
13
+ end
14
+ if (regress = example.metadata[:regression_mode])
15
+ Webhookdb.regression_mode = regress
16
+ end
17
+ end
18
+
19
+ context.after(:each) do |example|
20
+ if (mode = example.metadata[:whdbisolation])
21
+ Webhookdb::Organization.dataset.each(&:remove_related_database) if mode != :reset
22
+ Webhookdb::Organization::DbBuilder.reset_configuration
23
+ end
24
+ Webhookdb.regression_mode = false if example.metadata[:regression_mode]
25
+ end
26
+
27
+ context.around(:each) do |example|
28
+ if example.metadata[:fake_replicator]
29
+ Webhookdb::Replicator::Fake.reset
30
+ begin
31
+ example.run
32
+ ensure
33
+ Webhookdb::Replicator::Fake.reset
34
+ end
35
+ else
36
+ example.run
37
+ end
38
+ end
39
+
40
+ super
41
+ end
42
+
43
+ module_function def assign_connection_urls(o, **more)
44
+ u = Webhookdb::Organization::DbBuilder.available_server_urls.sample
45
+ raise "no server url?" if u.blank?
46
+ o.update(
47
+ readonly_connection_url_raw: u,
48
+ admin_connection_url_raw: u,
49
+ **more,
50
+ )
51
+ end
52
+
53
+ module_function def create_dependency(service_integration)
54
+ return service_integration.depends_on unless service_integration.depends_on.nil?
55
+ dependency_descriptor = service_integration.replicator.descriptor.dependency_descriptor
56
+ if dependency_descriptor.present?
57
+ dependency = Webhookdb::Fixtures.service_integration.create(
58
+ organization: service_integration.organization,
59
+ service_name: dependency_descriptor.name,
60
+ )
61
+ service_integration.update(depends_on: dependency)
62
+ return dependency
63
+ end
64
+ return nil
65
+ end
66
+
67
+ # If a service has dependencies, and those dependencies have dependencies, create
68
+ # them recursively until all requirements are satisfied.
69
+ module_function def create_all_dependencies(service_integration)
70
+ sint = service_integration
71
+ created = []
72
+ loop do
73
+ sint = create_dependency(sint)
74
+ break if sint.nil?
75
+ created << sint
76
+ end
77
+ return created
78
+ end
79
+
80
+ module_function def setup_dependencies(service_integration, insert_required_data_callback=nil)
81
+ service_integration.replicator.create_table
82
+ dep_sint = service_integration.depends_on
83
+ return [] if dep_sint.nil?
84
+ org = service_integration.organization
85
+ dep_repls = []
86
+ while dep_sint
87
+ # Make sure all service integration orgs are the same instance, since it may be mutated
88
+ # when we set up urls.
89
+ raise Webhookdb::InvariantViolation, "service integration orgs must match" unless
90
+ dep_sint.organization === org
91
+ dep_sint.organization = org
92
+ dep_repl = dep_sint.replicator
93
+ dep_repl.create_table
94
+ dep_repls << dep_repl
95
+ dep_sint = dep_sint.depends_on
96
+ break if dep_sint.nil?
97
+ end
98
+ insert_required_data_callback&.call(*dep_repls)
99
+ return dep_repls
100
+ end
101
+
102
+ module_function def setup_upsert_webhook_example(this)
103
+ this.let(:request_path) { nil }
104
+ this.let(:request_method) { nil }
105
+ this.let(:request_body) { nil }
106
+ this.let(:request_headers) { nil }
107
+ this.let(:webhook_request) do
108
+ Webhookdb::Replicator::WebhookRequest.new(
109
+ body: request_body, method: request_method, path: request_path, headers: request_headers,
110
+ )
111
+ end
112
+ this.define_method(:upsert_webhook) do |svc, **kw|
113
+ params = {body: request_body, headers: request_headers, method: request_method, path: request_path}
114
+ params.merge!(**kw)
115
+ svc.upsert_webhook(Webhookdb::Replicator::WebhookRequest.new(**params))
116
+ end
117
+ end
118
+
119
+ # Creates and returns a new backfill job for the given integration or replicator,
120
+ # and also runs the backfill.
121
+ module_function def backfill(sint, **kw)
122
+ sint = sint.service_integration if sint.respond_to?(:service_integration)
123
+ bfjob = Webhookdb::Fixtures.backfill_job.for(sint).create(**kw)
124
+ sint.replicator.backfill(bfjob)
125
+ return bfjob
126
+ end
127
+
128
+ def refresh_row(row, replicator: nil)
129
+ replicator ||= svc
130
+ raise "Must provide :replicator or have :svc available" if replicator.nil?
131
+ return replicator.readonly_dataset { |ds| ds[pk: row.fetch(:pk)] }
132
+ end
133
+
134
+ def update_row(row, replicator: nil, **fields)
135
+ replicator ||= svc
136
+ raise "Must provide :replicator or have :svc available" if replicator.nil?
137
+ return replicator.admin_dataset { |ds| ds.where(pk: row.fetch(:pk)).update(fields) }
138
+ end
139
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "oj"
4
+ require "webhookdb"
5
+
6
+ RSpec::Matchers.define_negated_matcher(:exclude, :include)
7
+ RSpec::Matchers.define_negated_matcher(:not_include, :include)
8
+ RSpec::Matchers.define_negated_matcher(:not_change, :change)
9
+ RSpec::Matchers.define_negated_matcher(:not_be_nil, :be_nil)
10
+ RSpec::Matchers.define_negated_matcher(:not_eq, :eq)
11
+ RSpec::Matchers.define_negated_matcher(:not_be_empty, :be_empty)
12
+
13
+ module Webhookdb::SpecHelpers
14
+ # The directory to look in for fixture data
15
+ TEST_DATA_DIR = Pathname("spec/data").expand_path
16
+
17
+ def self.included(context)
18
+ context.before(:all) do
19
+ Webhookdb::Customer.password_hash_cost = 1
20
+ end
21
+ context.before(:each) do
22
+ allow(Kernel).to receive(:sleep) do |n|
23
+ raise "Never sleep with > 0 during tests" if n.positive?
24
+ end
25
+ end
26
+ super
27
+ end
28
+
29
+ module_function def test_data_dir
30
+ return TEST_DATA_DIR
31
+ end
32
+
33
+ module_function def json_headers(**more)
34
+ return {"Content-Type" => "application/json"}.merge(**more)
35
+ end
36
+
37
+ module_function def json_response(body, status: 200, headers: {})
38
+ return {status:, body: body.to_json, headers: json_headers(**headers)}
39
+ end
40
+
41
+ ### Load data from the spec/data directory with the specified +name+,
42
+ ### deserializing it if it's YAML or JSON, and returning it.
43
+ module_function def load_fixture_data(name, raw: false)
44
+ name = name.to_s
45
+ path = TEST_DATA_DIR + name
46
+ path = TEST_DATA_DIR + "#{name}.json" unless path.exist? || !File.extname(name).empty?
47
+ path = TEST_DATA_DIR + "#{name}.yaml" unless path.exist? || !File.extname(name).empty?
48
+ path = TEST_DATA_DIR + "#{name}.yml" unless path.exist? || !File.extname(name).empty?
49
+
50
+ rawdata = path.read(encoding: "utf-8")
51
+
52
+ return rawdata if raw
53
+
54
+ return case path.extname
55
+ when ".json"
56
+ Oj.load(rawdata)
57
+ when ".yml", ".yaml"
58
+ YAML.safe_load(rawdata)
59
+ else
60
+ rawdata
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+
5
+ module Webhookdb::Sponsy
6
+ include Appydays::Configurable
7
+
8
+ configurable(:sponsy) do
9
+ setting :cron_expression, "*/30 */4 * * *" # “At every 30th minute past every 4th hour.”
10
+ setting :http_timeout, 30
11
+ setting :page_size, 100
12
+ setting :parallel_backfill, 3
13
+ end
14
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Webhookdb::Stripe
4
+ extend Webhookdb::MethodUtilities
5
+ include Appydays::Configurable
6
+ include Appydays::Loggable
7
+
8
+ configurable(:stripe) do
9
+ setting :api_key, "whdb_stripe_api_key", key: "STRIPE_API_KEY"
10
+ setting :http_timeout, 30
11
+ setting :webhook_secret, "whdb_stripe_webhook_secret", key: "STRIPE_WEBHOOK_SECRET"
12
+
13
+ after_configured do
14
+ ::Stripe.api_key = self.api_key
15
+ end
16
+ end
17
+
18
+ def self.webhook_response(request, webhook_secret)
19
+ auth = request.env["HTTP_STRIPE_SIGNATURE"]
20
+
21
+ return Webhookdb::WebhookResponse.error("missing hmac") if auth.nil?
22
+
23
+ request.body.rewind
24
+ request_data = request.body.read
25
+
26
+ begin
27
+ Stripe::Webhook.construct_event(
28
+ request_data, auth, webhook_secret,
29
+ )
30
+ rescue Stripe::SignatureVerificationError => e
31
+ self.logger.warn "stripe_signature_verification_error", message: e.to_s
32
+ return Webhookdb::WebhookResponse.error("invalid hmac")
33
+ end
34
+
35
+ return Webhookdb::WebhookResponse.ok
36
+ end
37
+ end
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Webhookdb::Subscription < Webhookdb::Postgres::Model(:subscriptions)
4
+ extend Webhookdb::MethodUtilities
5
+ include Appydays::Configurable
6
+ include Appydays::Loggable
7
+
8
+ class Plan
9
+ attr_accessor :key, :description, :price, :stripe_price_id, :stripe_product_id
10
+
11
+ def initialize(key, stripe_price)
12
+ @key = key
13
+ @description = stripe_price.nickname
14
+ @price = Money.new(stripe_price.unit_amount)
15
+ @stripe_price_id = stripe_price.id
16
+ @stripe_product_id = stripe_price.product
17
+ end
18
+
19
+ def as_json
20
+ return {key:, description:, price:}
21
+ end
22
+ end
23
+
24
+ plugin :timestamps
25
+ plugin :soft_deletes
26
+
27
+ configurable(:subscription) do
28
+ setting :billing_enabled, false
29
+ setting :free_integrations_with_billing, 2
30
+ end
31
+
32
+ class << self
33
+ def billing_enabled? = self.billing_enabled
34
+ def billing_disabled? = !self.billing_enabled?
35
+ def max_free_integrations = self.billing_enabled? ? self.free_integrations_with_billing : 9999
36
+ end
37
+
38
+ one_to_one :organization, class: "Webhookdb::Organization", key: :stripe_customer_id, primary_key: :stripe_customer_id
39
+
40
+ def self.list_plans
41
+ return [] if self.billing_disabled?
42
+ prices = Stripe::Price.list(active: true)
43
+ monthly = prices.find { |pr| pr.recurring.interval == "month" }
44
+ yearly = prices.find { |pr| pr.recurring.interval == "year" }
45
+ raise "Expected month and year prices in: #{prices.to_json}" unless monthly && yearly
46
+ return [Plan.new("monthly", monthly), Plan.new("yearly", yearly)]
47
+ end
48
+
49
+ def initialize(*)
50
+ super
51
+ self[:stripe_json] ||= Sequel.pg_json({})
52
+ end
53
+
54
+ def status
55
+ return self.stripe_json["status"]
56
+ end
57
+
58
+ def plan_name
59
+ return self.stripe_json.dig("plan", "nickname") || ""
60
+ end
61
+
62
+ def self.create_or_update_from_stripe_hash(obj)
63
+ created = false
64
+ orig_status = nil
65
+ sub = self.update_or_create(stripe_id: obj.fetch("id")) do |o|
66
+ o.stripe_customer_id = obj.fetch("customer")
67
+ if o.new?
68
+ created = true
69
+ else
70
+ orig_status = o.status
71
+ end
72
+ o.stripe_json = obj.to_json
73
+ end
74
+ common_fields = [
75
+ {title: "Subscription", value: sub.id, short: true},
76
+ {title: "Status", value: sub.status, short: true},
77
+ {title: "Stripe ID", value: sub.stripe_id, short: true},
78
+ {title: "Customer ID", value: sub.stripe_customer_id, short: true},
79
+ ]
80
+ if sub.organization.nil?
81
+ Webhookdb::DeveloperAlert.new(
82
+ subsystem: "Subscription Error",
83
+ emoji: ":hook:",
84
+ fallback: "Subscription with Stripe ID #{sub.stripe_id} has no organization",
85
+ fields: common_fields + [
86
+ {title: "Message", value: "Has no organization in WebhookDB", short: false},
87
+ ],
88
+ ).emit
89
+ elsif created
90
+ Webhookdb::DeveloperAlert.new(
91
+ subsystem: "Subscription Created",
92
+ emoji: ":hook:",
93
+ fallback: "Subscription with Stripe ID #{sub.stripe_id} created",
94
+ fields: common_fields + [
95
+ {title: "Organization", value: sub.organization.display_string, short: true},
96
+ {title: "Message", value: "Created", short: true},
97
+ ],
98
+ ).emit
99
+ elsif orig_status != sub.status
100
+ Webhookdb::DeveloperAlert.new(
101
+ subsystem: "Subscription Status Change",
102
+ emoji: ":hook:",
103
+ fallback: "Subscription with Stripe ID #{sub.stripe_id} changed status",
104
+ fields: common_fields + [
105
+ {title: "Organization", value: sub.organization.display_string, short: true},
106
+ {title: "Message", value: "Status updated", short: true},
107
+ ],
108
+ ).emit
109
+ end
110
+ return sub
111
+ end
112
+
113
+ def self.create_or_update_from_webhook(webhook_body)
114
+ obj = webhook_body["data"]["object"]
115
+ self.create_or_update_from_stripe_hash(obj)
116
+ end
117
+
118
+ def self.create_or_update_from_id(id)
119
+ subscription_obj = Stripe::Subscription.retrieve(id)
120
+ self.create_or_update_from_stripe_hash(subscription_obj.as_json)
121
+ end
122
+
123
+ class Status
124
+ attr_reader :data
125
+
126
+ def initialize(**kw)
127
+ @data = kw
128
+ end
129
+
130
+ def display_headers
131
+ return [
132
+ [:organization_formatted, "Organization"],
133
+ [:billing_email, "Billing email"],
134
+ [:plan_name, "Plan name"],
135
+ [:integrations_used_formatted, "Integrations used"],
136
+ [:integrations_remaining_formatted, "Integrations left"],
137
+ [:sub_status, "Status"],
138
+ ]
139
+ end
140
+
141
+ def message
142
+ return "Use `webhookdb subscription edit` to set up or modify your subscription."
143
+ end
144
+
145
+ def as_json(*_o)
146
+ return @data.merge(message: self.message, display_headers: self.display_headers)
147
+ end
148
+ end
149
+
150
+ def self.status_for_org(org)
151
+ service_integrations = org.service_integrations
152
+ used = service_integrations.count
153
+ data = {
154
+ organization_name: org.name,
155
+ organization_key: org.key,
156
+ organization_formatted: org.display_string,
157
+ billing_email: org.billing_email,
158
+ integrations_used: used,
159
+ integrations_used_formatted: used.to_s,
160
+ }
161
+ subscription = Webhookdb::Subscription[stripe_customer_id: org.stripe_customer_id]
162
+ # TODO: Modify the Stripe JSON to store the values of the fields for paid plans,
163
+ # rather than hard-coding them.
164
+ if subscription.nil?
165
+ data[:plan_name] = "Free"
166
+ data[:integrations_remaining] = [0, Webhookdb::Subscription.max_free_integrations - used].max
167
+ data[:integrations_remaining_formatted] = data[:integrations_remaining].to_s
168
+ data[:sub_status] = ""
169
+ else
170
+ data[:plan_name] = subscription.plan_name
171
+ data[:integrations_remaining] = 2_000_000_000
172
+ data[:integrations_remaining_formatted] = "unlimited"
173
+ data[:sub_status] = subscription.status
174
+ end
175
+ return Status.new(**data)
176
+ end
177
+
178
+ def self.backfill_from_stripe(limit: 50, page_size: 50)
179
+ subs = Stripe::Subscription.list({limit: page_size})
180
+ done = 0
181
+ subs.auto_paging_each do |sub|
182
+ self.create_or_update_from_stripe_hash(sub.as_json)
183
+ done += 1
184
+ break if !limit.nil? && done >= limit
185
+ end
186
+ end
187
+ end
188
+
189
+ # Table: subscriptions
190
+ # ---------------------------------------------------------------------------------------------
191
+ # Columns:
192
+ # id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
193
+ # created_at | timestamp with time zone | NOT NULL DEFAULT now()
194
+ # updated_at | timestamp with time zone |
195
+ # soft_deleted_at | timestamp with time zone |
196
+ # stripe_id | text | NOT NULL
197
+ # stripe_customer_id | text | NOT NULL DEFAULT ''::text
198
+ # stripe_json | jsonb | DEFAULT '{}'::jsonb
199
+ # Indexes:
200
+ # subscriptions_pkey | PRIMARY KEY btree (id)
201
+ # subscriptions_stripe_id_key | UNIQUE btree (stripe_id)
202
+ # subscriptions_stripe_customer_id_index | btree (stripe_customer_id)
203
+ # ---------------------------------------------------------------------------------------------