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,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/cors"
4
+ require "rack/protection"
5
+ require "rack/ssl-enforcer"
6
+ require "sentry-ruby"
7
+ require "appydays/loggable/request_logger"
8
+
9
+ require "webhookdb/service" unless defined?(Webhookdb::Service)
10
+
11
+ module Webhookdb::Service::Middleware
12
+ def self.add_middlewares(builder)
13
+ self.add_cors_middleware(builder)
14
+ self.add_common_middleware(builder)
15
+ self.add_dev_middleware(builder) if Webhookdb::Service.devmode
16
+ self.add_ssl_middleware(builder) if Webhookdb::Service.enforce_ssl
17
+ self.add_session_middleware(builder)
18
+ self.add_security_middleware(builder)
19
+ Webhookdb::Service::Auth.add_warden_middleware(builder)
20
+ builder.use(RequestLogger)
21
+ end
22
+
23
+ def self.add_cors_middleware(builder)
24
+ builder.use(Rack::Cors) do
25
+ allow do
26
+ origins(*Webhookdb::Service.cors_origins)
27
+ resource "*",
28
+ headers: :any,
29
+ methods: :any,
30
+ credentials: true,
31
+ expose: ["ETag", Webhookdb::Service::AUTH_TOKEN_HEADER]
32
+ end
33
+ end
34
+ end
35
+
36
+ def self.add_common_middleware(builder)
37
+ builder.use(Rack::ContentLength)
38
+ builder.use(Rack::Chunked)
39
+ builder.use(Sentry::Rack::CaptureExceptions)
40
+ end
41
+
42
+ def self.add_dev_middleware(builder)
43
+ builder.use(Rack::ShowExceptions)
44
+ builder.use(Rack::Lint)
45
+ end
46
+
47
+ def self.add_ssl_middleware(builder)
48
+ builder.use(Rack::SslEnforcer, redirect_html: false)
49
+ end
50
+
51
+ ### Add middleware for maintaining sessions to +builder+.
52
+ def self.add_session_middleware(builder)
53
+ builder.use CopyCookieToExplicitHeader
54
+ builder.use Rack::Session::Cookie, Webhookdb::Service.cookie_config
55
+ builder.use SessionLength
56
+ builder.use(SessionReader)
57
+ end
58
+
59
+ ### Add security middleware to +builder+.
60
+ def self.add_security_middleware(_builder)
61
+ # session_hijacking causes issues in integration tests...?
62
+ # builder.use Rack::Protection, except: :session_hijacking
63
+ end
64
+
65
+ # We always want a session to be written, even if noop requests,
66
+ # so always force a write if the session isn't loaded.
67
+ class SessionReader
68
+ def initialize(app)
69
+ @app = app
70
+ end
71
+
72
+ def call(env)
73
+ env["rack.session"]["_"] = "_" unless env["rack.session"].loaded?
74
+ @app.call(env)
75
+ end
76
+ end
77
+
78
+ # In browser environments, they can't access the Cookie header for API requests,
79
+ # and it doesn't always get the automatic binding behavior we want
80
+ # (for example fetches made from WASM).
81
+ # So copy it into something we can access.
82
+ # This must be before the Rack session middleware.
83
+ class CopyCookieToExplicitHeader
84
+ def initialize(app)
85
+ @app = app
86
+ end
87
+
88
+ def call(env)
89
+ env["HTTP_COOKIE"] = env[Webhookdb::Service::AUTH_TOKEN_HTTP] if env[Webhookdb::Service::AUTH_TOKEN_HTTP]
90
+ status, headers, body = @app.call(env)
91
+ headers[Webhookdb::Service::AUTH_TOKEN_HEADER] = headers["Set-Cookie"] if headers["Set-Cookie"]
92
+ return status, headers, body
93
+ end
94
+ end
95
+
96
+ # In some environments, like the browser, we may want a shorter session.
97
+ # Use a dedicated header to provide a short session.
98
+ class SessionLength
99
+ def initialize(app)
100
+ @app = app
101
+ end
102
+
103
+ def call(env)
104
+ env["rack.session.options"][:expire_after] = 2.hours.to_i if
105
+ env[Webhookdb::Service::SHORT_SESSION_HTTP].present?
106
+ return @app.call(env)
107
+ end
108
+ end
109
+
110
+ class RequestLogger < Appydays::Loggable::RequestLogger
111
+ def request_tags(env)
112
+ tags = super
113
+ begin
114
+ tags[:customer_id] = env["warden"].user(:customer)&.id || 0
115
+ rescue Sequel::DatabaseError
116
+ # If we cant hit the database, ignore this for now.
117
+ # We run this code on all code paths, including those that don't need the customer,
118
+ # and we want those to run even if the DB is down (like health checks, for example).
119
+ nil
120
+ end
121
+ return tags
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "grape"
4
+
5
+ module Webhookdb::Service::Types
6
+ def self.included(ctx)
7
+ ctx.const_set(:NormalizedEmail, NormalizedEmail)
8
+ ctx.const_set(:NormalizedPhone, NormalizedPhone)
9
+ ctx.const_set(:CommaSepArray, CommaSepArray)
10
+ end
11
+
12
+ class NormalizedEmail
13
+ def self.parse(value)
14
+ return value.downcase.strip
15
+ end
16
+ end
17
+
18
+ class NormalizedPhone
19
+ def self.parse(value)
20
+ return Webhookdb::PhoneNumber::US.normalize(value)
21
+ end
22
+ end
23
+
24
+ class CommaSepArray
25
+ def self.parse(value)
26
+ return value if value.respond_to?(:to_ary)
27
+ return value.split(",").map(&:strip)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "grape"
4
+ require "grape/validations/validators/base"
5
+
6
+ module Webhookdb::Service::Validators
7
+ class UsPhone < ::Grape::Validations::Validators::Base
8
+ def validate_param!(attr_name, params)
9
+ val = params[attr_name]
10
+ return if val.blank? && @allow_blank
11
+ return if Webhookdb::PhoneNumber::US.valid?(val)
12
+ raise Grape::Exceptions::Validation.new(
13
+ params: [@scope.full_name(attr_name)],
14
+ message: "must be a 10-digit US phone",
15
+ )
16
+ end
17
+ end
18
+
19
+ class DbIdentifier < ::Grape::Validations::Validators::Base
20
+ def validate_param!(attr_name, params)
21
+ val = params[attr_name]
22
+ return if val.blank? && @allow_blank
23
+ re = Webhookdb::DBAdapter::VALID_IDENTIFIER
24
+ return if re.match?(val)
25
+ raise Grape::Exceptions::Validation.new(
26
+ params: [@scope.full_name(attr_name)],
27
+ message: "is not a valid database identifier for WebhookDB. " +
28
+ Webhookdb::DBAdapter::INVALID_IDENTIFIER_MESSAGE,
29
+ )
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Mixin for Grape API endpoints that use HTML rendering.
4
+ # This isn't tested well enough.
5
+ module Webhookdb::Service::ViewApi
6
+ class FormError < StandardError
7
+ attr_reader :status
8
+
9
+ def initialize(msg, status=400)
10
+ @status = status
11
+ super(msg)
12
+ end
13
+ end
14
+
15
+ def self.included(mod)
16
+ mod.helpers do
17
+ define_method(:render_liquid) do |
18
+ data_rel_path,
19
+ vars: {},
20
+ content_type: "text/html",
21
+ # If true, store view params in a cookie so we can re-render this page easily, usually to show errors.
22
+ serialize_view_params: false
23
+ |
24
+ tmpl_file = File.open(Webhookdb::DATA_DIR + data_rel_path)
25
+ liquid_tmpl = Liquid::Template.parse(tmpl_file.read)
26
+ rendered = liquid_tmpl.render!(vars.stringify_keys, registers: {})
27
+ _endpoint.content_type content_type
28
+ if serialize_view_params
29
+ _endpoint.cookies[:whdbviewparams] = {path: data_rel_path, vars:, content_type:}.to_json
30
+ end
31
+ env["api.format"] = :binary
32
+ rendered
33
+ end
34
+
35
+ define_method(:_endpoint) do
36
+ env["api.endpoint"]
37
+ end
38
+ end
39
+
40
+ mod.rescue_from FormError do |e|
41
+ if (params = _endpoint.cookies[:whdbviewparams]).blank?
42
+ merror!(e.status, e.message)
43
+ else
44
+ begin
45
+ h = JSON.parse(params)
46
+ rescue StandardException
47
+ # If there are any problems, use fallback error handling.
48
+ merror!(e.status, e.message)
49
+ else
50
+ vars = h["vars"].symbolize_keys
51
+ vars[:error_message] = e.message
52
+ new_html = render_liquid(
53
+ h.fetch("path"),
54
+ vars:,
55
+ content_type: h.fetch("content_type"),
56
+ serialize_view_params: true,
57
+ )
58
+ Rack::Response.new(new_html, e.status, {"Content-Type" => "text/html"})
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,219 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+ require "grape"
5
+ require "appydays/loggable"
6
+ require "pg"
7
+ require "sentry-ruby"
8
+ require "sequel"
9
+ require "sequel/adapters/postgres"
10
+ require "warden"
11
+
12
+ require "webhookdb"
13
+ require "webhookdb/platform"
14
+
15
+ # Service is the base class for all endpoint/resource classes.
16
+ class Webhookdb::Service < Grape::API
17
+ extend Webhookdb::MethodUtilities
18
+ include Appydays::Configurable
19
+ include Appydays::Loggable
20
+
21
+ require "webhookdb/service/auth"
22
+ require "webhookdb/service/middleware"
23
+ require "webhookdb/service/types"
24
+ require "webhookdb/service/validators"
25
+
26
+ # Name of the session in the server response cookie.
27
+ # Note that it is always 'rack.session' in code though.
28
+ SESSION_COOKIE = "webhookdb.session"
29
+ AUTH_TOKEN_HEADER = "Whdb-Auth-Token"
30
+ AUTH_TOKEN_HTTP = "HTTP_WHDB_AUTH_TOKEN"
31
+ SHORT_SESSION_HEADER = "Whdb-Short-Session"
32
+ SHORT_SESSION_HTTP = "HTTP_WHDB_SHORT_SESSION"
33
+ DEFAULT_CORS_ORIGINS = [/localhost:\d+/, /192\.168\.\d{1,3}\.\d{1,3}:\d{3,5}/].freeze
34
+
35
+ configurable(:service) do
36
+ setting :max_session_age, 90.days.to_i
37
+
38
+ # Note that changing the secret would invalidate all existing sessions!
39
+ setting :session_secret, "Tritiphamhockbiltongpigporkchoptbonebeefsala" \
40
+ "michickenmeatballKielbasajowldrumstickbeefri" \
41
+ "bsfiletmignonbiltongPorkbellyballtipbacontai" \
42
+ "lgroundroundshankDrumstickcornedbeefbiltongp" \
43
+ "ancettaTbone"
44
+
45
+ # Must be nil/unset on localhost, otherwise set to something.
46
+ setting :cookie_domain, ""
47
+
48
+ setting :devmode, false
49
+
50
+ setting :enforce_ssl, true
51
+
52
+ setting :cors_origins, [],
53
+ convert: lambda { |origin_str|
54
+ origin_str.split.map { |s| %r{/.+/}.match?(s) ? Regexp.new(s[1..-2]) : s }
55
+ }
56
+
57
+ setting :endpoint_caching, false
58
+
59
+ after_configured do
60
+ self.cors_origins += DEFAULT_CORS_ORIGINS
61
+ end
62
+ end
63
+
64
+ # Return the config for the Rack::Session::Cookie middleware.
65
+ def self.cookie_config
66
+ return {
67
+ key: Webhookdb::Service::SESSION_COOKIE,
68
+ domain: Webhookdb::Service.cookie_domain,
69
+ path: "/",
70
+ expire_after: Webhookdb::Service.max_session_age,
71
+ secret: Webhookdb::Service.session_secret,
72
+ coder: Rack::Session::Cookie::Base64::ZipJSON.new,
73
+ }
74
+ end
75
+
76
+ def self.decode_cookie(s)
77
+ cfg = self.cookie_config
78
+ s = s.delete_prefix(cfg[:key] + "=")
79
+ return cfg[:coder].decode(Rack::Utils.unescape(s))
80
+ end
81
+
82
+ ### Build the Rack app according to the configured environment.
83
+ def self.build_app
84
+ inner_app = self
85
+ builder = Rack::Builder.new do
86
+ Webhookdb::Service::Middleware.add_middlewares(self)
87
+ run inner_app
88
+ end
89
+ return builder.to_app
90
+ end
91
+
92
+ def self.error_body(status, message, code: nil, more: {})
93
+ error = more.merge(
94
+ message:,
95
+ status:,
96
+ )
97
+ error[:code] = code unless code.nil?
98
+ return {error:}
99
+ end
100
+
101
+ Grape::Middleware::Auth::Strategies.add(:admin, Webhookdb::Service::Auth::Admin)
102
+ Grape::Middleware::Auth::Strategies.add(:skip, Webhookdb::Service::Auth::Skip)
103
+
104
+ # Add some context to Sentry on each request.
105
+ before do
106
+ customer = current_customer?
107
+ admin = admin_customer?
108
+ Sentry.configure_scope do |scope|
109
+ sentry_tags = {
110
+ agent: Webhookdb::Platform.user_agent(env),
111
+ host: env["HTTP_HOST"],
112
+ method: env["REQUEST_METHOD"],
113
+ path: env["PATH_INFO"],
114
+ query: env["QUERY_STRING"],
115
+ referrer: env["HTTP_REFERER"],
116
+ }
117
+ if (platform_ua = Webhookdb::Platform.platform_user_agent(env))
118
+ sentry_tags[:platform_user_agent] = platform_ua
119
+ end
120
+ sentry_user = {ip_address: request.ip}
121
+ if customer
122
+ sentry_user.merge!(
123
+ id: customer.id,
124
+ email: customer.email,
125
+ name: customer.name,
126
+ ip_address: request.ip,
127
+ )
128
+ sentry_tags["customer.email"] = customer.email
129
+ end
130
+ if admin
131
+ sentry_user.merge!(
132
+ admin_id: admin.id,
133
+ admin_email: admin.email,
134
+ admin_name: admin.name,
135
+ )
136
+ sentry_tags["admin.email"] = admin.email
137
+ end
138
+ scope.set_user(sentry_user)
139
+ sentry_tags.delete_if { |_, v| v.blank? }
140
+ scope.set_tags(sentry_tags)
141
+ end
142
+
143
+ Webhookdb.set_request_user_and_admin(customer, admin)
144
+ end
145
+
146
+ rescue_from Grape::Exceptions::ValidationErrors do |e|
147
+ # We can flesh this out with more details later:
148
+ # https://github.com/ruby-grape/grape#validation-errors
149
+ # https://stripe.com/docs/api/curl#errors
150
+ field_errors = e.each_with_object({}) do |(params, message), memo|
151
+ params.each do |p|
152
+ (memo[p] ||= []) << message
153
+ end
154
+ end
155
+ invalid_fields!(field_errors, message: e.message.upcase_first)
156
+ end
157
+
158
+ rescue_from Webhookdb::Customer::InvalidPassword do |e|
159
+ invalid_fields!({"password" => [e.message]}, message: e.message.upcase_first)
160
+ end
161
+
162
+ rescue_from Grape::Exceptions::MethodNotAllowed do |e|
163
+ error!(e.message, 405)
164
+ end
165
+
166
+ rescue_from Webhookdb::LockFailed do |_e|
167
+ merror!(
168
+ 409,
169
+ "Attempting to lock the resource failed. You should fetch a new version of the resource and try again.",
170
+ code: "lock_failed",
171
+ )
172
+ end
173
+
174
+ rescue_from Webhookdb::InvalidInput do |e|
175
+ merror!(400, e.message, code: "invalid_input")
176
+ end
177
+
178
+ rescue_from Webhookdb::Organization::DatabaseMigration::MigrationInProgress do |e|
179
+ merror!(409, e.message, code: "migration_in_progress")
180
+ end
181
+
182
+ rescue_from Webhookdb::DatabaseLocked do |e|
183
+ merror!(409, e.message, code: "database_locked")
184
+ end
185
+
186
+ rescue_from Sequel::ValidationFailed do |e|
187
+ invalid_fields!(e.errors, message: e.message.upcase_first)
188
+ end
189
+
190
+ rescue_from :all do |e|
191
+ status = e.respond_to?(:status) ? e.status : 500
192
+ error_id = SecureRandom.uuid
193
+ error_signature = Digest::MD5.hexdigest("#{e.class}: #{e.message}")
194
+
195
+ Webhookdb::Service.logger.error "[%s] Uncaught %p in service: %s" %
196
+ [error_id, e.class, e.message]
197
+ Webhookdb::Service.logger.debug { e.backtrace.join("\n") }
198
+ if ENV["PRINT_API_ERROR"]
199
+ puts e
200
+ puts e.backtrace
201
+ end
202
+
203
+ more = {error_id:, error_signature:}
204
+
205
+ if Webhookdb::Service.devmode
206
+ msg = e.message
207
+ more[:backtrace] = e.backtrace.join("\n")
208
+ else
209
+ Sentry.capture_exception(e, tags: more) if Webhookdb::Sentry.enabled?
210
+ msg = "An internal error occurred of type #{error_signature}. Error ID: #{error_id}"
211
+ end
212
+ Webhookdb::Service.logger.error("api_exception", {error_id:, error_signature:}, e)
213
+ merror!(status, msg, code: "api_error", more:)
214
+ end
215
+
216
+ finally do
217
+ Webhookdb.set_request_user_and_admin(nil, nil)
218
+ end
219
+ end