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,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/async/job"
4
+ require "stripe"
5
+ require "webhookdb/stripe"
6
+
7
+ # All the 'webhookdb' integrations are internal, so this endpoint is not hardened thoroughly.
8
+ # If we were to open this up or use it on prod, we need to harden it to support
9
+ # more and slower endpoints (at least by fanning out the work).
10
+ class Webhookdb::Jobs::WebhookdbResourceNotifyIntegrations
11
+ extend Webhookdb::Async::Job
12
+
13
+ # As we add more resources, modify this wildcard
14
+ on "webhookdb.customer.created"
15
+
16
+ sidekiq_options queue: "netout"
17
+
18
+ def _perform(event)
19
+ cu = self.lookup_model(Webhookdb::Customer, event)
20
+ Webhookdb::ServiceIntegration.where(service_name: "webhookdb_customer_v1").each do |sint|
21
+ Webhookdb::Http.post(
22
+ sint.replicator.webhook_endpoint,
23
+ cu.values,
24
+ headers: {"Whdb-Secret" => sint.webhook_secret},
25
+ logger: self.logger,
26
+ timeout: nil,
27
+ )
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "amigo/queue_backoff_job"
4
+ require "amigo/durable_job"
5
+ require "webhookdb/async/job"
6
+ require "webhookdb/jobs"
7
+
8
+ class Webhookdb::Jobs::WebhookSubscriptionDeliveryEvent
9
+ include Sidekiq::Worker
10
+ include Amigo::DurableJob
11
+ include Amigo::QueueBackoffJob
12
+
13
+ sidekiq_options queue: "netout"
14
+
15
+ def dependent_queues
16
+ return ["critical"]
17
+ end
18
+
19
+ def perform(delivery_id)
20
+ delivery = Webhookdb::WebhookSubscription::Delivery[delivery_id]
21
+ Webhookdb::Async::JobLogger.with_log_tags(
22
+ webhook_subscription_delivery_id: delivery.id,
23
+ webhook_subscription_id: delivery.webhook_subscription_id,
24
+ organization_key: delivery.webhook_subscription.fetch_organization,
25
+ ) do
26
+ delivery.attempt_delivery
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Webhookdb::Jobs
4
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "oj"
5
+ require "active_support/json"
6
+
7
+ module Webhookdb
8
+ # Helpers for JSON encoding and (to a lesser degree) decoding.
9
+ # In general, decoding JSON in Ruby is simple, as long as we
10
+ # don't try and deal with automatically converting types (which we don't).
11
+ #
12
+ # But encoding JSON is complex, like for example encoding a Time.
13
+ # ActiveSupport uses #as_json to convert Ruby types to JSON-native types.
14
+ # In order to use this, we must call Webhookdb::Json.encode,
15
+ # or go through ActiveSupport (which we patch here).
16
+ #
17
+ # That is, using JSON.dump(Time.now) would give you '"1970-01-01 02:00:00 +0200"'
18
+ # but using Webhookdb::Json.encode(Time.now) gives you '"1970-01-01T02:00:00.123+02:00"'
19
+ #
20
+ # Anyway, this is all largely under the hood and handled for us using ActiveSupport,
21
+ # but while we switched Yajl to Oj, we went ahead and added this shim to bypass
22
+ # the worst part of ActiveSupport (its still very tied to Rails needs).
23
+ module Json
24
+ DEFAULT_OPTIONS = {}.freeze
25
+ PRETTY_OPTIONS = {indent: " ", space: " ", object_nl: "\n", array_nl: "\n"}.freeze
26
+
27
+ # Based on ActiveSupport::JSON::Encoding::JSONGemEncoder.
28
+ # Removes the HTML escaping code (which is slow), but otherwise continues to depend
29
+ # on the as_json approach.
30
+ class Encoder
31
+ attr_reader :options
32
+
33
+ def initialize(options=nil)
34
+ @options = options || DEFAULT_OPTIONS
35
+ end
36
+
37
+ def encode(value)
38
+ return stringify(jsonify(value.as_json(@options.dup)))
39
+ end
40
+
41
+ # Convert an object into a "JSON-ready" representation composed of
42
+ # primitives like Hash, Array, String, Numeric,
43
+ # and +true+/+false+/+nil+.
44
+ # Recursively calls #as_json to the object to recursively build a
45
+ # fully JSON-ready object.
46
+ #
47
+ # This allows developers to implement #as_json without having to
48
+ # worry about what base types of objects they are allowed to return
49
+ # or having to remember to call #as_json recursively.
50
+ #
51
+ # Note: the +options+ hash passed to +object.to_json+ is only passed
52
+ # to +object.as_json+, not any of this method's recursive +#as_json+
53
+ # calls.
54
+ def jsonify(value)
55
+ case value
56
+ when Rational
57
+ value.to_s
58
+ when String, Numeric, NilClass, TrueClass, FalseClass
59
+ value.as_json
60
+ when Hash
61
+ result = {}
62
+ value.each do |k, v|
63
+ result[jsonify(k)] = jsonify(v)
64
+ end
65
+ result
66
+ when Array
67
+ value.map { |v| jsonify(v) }
68
+ else
69
+ jsonify value.as_json
70
+ end
71
+ end
72
+
73
+ def stringify(jsonified)
74
+ # If this breaks because we actually need to handle more types,
75
+ # add them to jsonify, like Rational.
76
+ #
77
+ # We can't use `mode: :rails` here since we can't control
78
+ # whether to use html-escaping (we don't want to use it) without using `Oj.optimize_rails`,
79
+ # which causes other problems- we want to use ISO8601 encoding with millsecond precision,
80
+ # but we get JSON-gem-style formatting (even if setting flags to modify ActiveSupport params)
81
+ # when we use `optimize-rails` (but not mode: :rails, since we stringify the time beforehand).
82
+ return ::Oj.dump(jsonified, mode: :strict, **@options)
83
+ end
84
+ end
85
+
86
+ class << self
87
+ # Dump as compact, standard JSON, using iso8601 for times.
88
+ def encode(value, options=nil)
89
+ Encoder.new(options).encode(value)
90
+ end
91
+
92
+ # Dump as pretty JSON, similar to JSON.pretty_generate but with iso8601 times.
93
+ def pretty_generate(value)
94
+ Webhookdb::Json.encode(value, PRETTY_OPTIONS)
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ # This stomps the JSON.load, etc., methods, to use the faster (and still compatible) Oj version.
101
+ Oj.mimic_JSON
102
+
103
+ # Use the shim encoder rather than the ActiveSupport one.
104
+ # This mostly handles #to_json calls.
105
+ ActiveSupport::JSON::Encoding.json_encoder = Webhookdb::Json::Encoder
106
+
107
+ # Replace ActiveSupport::JSON.encode so it calls Oj directly.
108
+ # This isn't strictly necessary (since we set json_encoder), but skips an extra method call.
109
+ module ActiveSupport::JSON
110
+ def self.encode(value, options=nil)
111
+ return Webhookdb::Json.encode(value, options)
112
+ end
113
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "liquid"
4
+
5
+ require "webhookdb"
6
+
7
+ # Allow customers to expose variables from a template
8
+ # using custom blocks and registers.
9
+ # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#create-your-own-tag-blocks
10
+ # for more info about blocks.
11
+ # See https://github.com/Shopify/liquid/wiki/Liquid-for-Programmers#difference-between-assigns-and-registers
12
+ # for info about "registers", which are used as template-render-specific mutable state
13
+ # (so we can mutate it in the tag/block, then inspect the mutated value after-the-fact).
14
+ class Webhookdb::Liquid::Expose < Liquid::Block
15
+ def initialize(tag_name, var_name, options)
16
+ super
17
+ @var_name = var_name.strip.to_sym
18
+ end
19
+
20
+ def render(context)
21
+ content = super
22
+ context.registers[@var_name] = content
23
+ ""
24
+ end
25
+ end
26
+
27
+ Liquid::Template.register_tag("expose", Webhookdb::Liquid::Expose)
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "liquid"
4
+ require "webhookdb"
5
+
6
+ module Webhookdb::Liquid::Filters
7
+ def humanize(input)
8
+ return input.humanize
9
+ end
10
+
11
+ def money(input)
12
+ return input.format
13
+ end
14
+ end
15
+
16
+ Liquid::Template.register_filter(Webhookdb::Liquid::Filters)
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Liquification provides a to_liquid method that returns a wrapped object,
4
+ # so that any object can be used in a liquid template
5
+ # (normally only things like basic types, and objects with to_liquid,
6
+ # can be used in templates). This allows us to use Liquid filters for rendering
7
+ # custom types, rather than presenters. We prefer filters because
8
+ # it keeps display logic in the view, rather than the backing 'template'
9
+ # having to choose the presenter.
10
+ class Webhookdb::Liquid::Liquification
11
+ def initialize(wrapped)
12
+ @wrapped = wrapped
13
+ end
14
+
15
+ def method_missing(m, *, &)
16
+ return @wrapped.respond_to?(m) ? @wrapped.send(m, *, &) : super
17
+ end
18
+
19
+ def respond_to_missing?(m, include_private: false)
20
+ return super || @wrapped.respond_to?(m)
21
+ end
22
+
23
+ def to_liquid
24
+ return self
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb"
4
+ require "liquid"
5
+
6
+ class Webhookdb::Liquid::Partial < Liquid::Include
7
+ def initialize(tag_name, name, options)
8
+ name = "'partials/#{Regexp.last_match(1)}'" if name =~ /['"]([a-z0-9_]+)['"]/
9
+ super(tag_name, name, options)
10
+ end
11
+ end
12
+ Liquid::Template.register_tag("partial", Webhookdb::Liquid::Partial)
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Webhookdb::LoggedWebhook::Resilient
4
+ def logger = Webhookdb::LoggedWebhook.logger
5
+
6
+ def database_urls = Webhookdb::LoggedWebhook.available_resilient_database_urls
7
+
8
+ def insert(kwargs)
9
+ return Webhookdb::LoggedWebhook.dataset.insert(kwargs)
10
+ rescue Sequel::DatabaseError => e
11
+ service_integration_opaque_id = kwargs.fetch(:service_integration_opaque_id)
12
+ str_payload = JSON.dump(kwargs)
13
+ self.database_urls.each do |url|
14
+ next unless self.write_to(url, service_integration_opaque_id, str_payload)
15
+ self.logger.warn "resilient_insert_handled", error: e, **self._dburl_log_kwargs(url)
16
+ return true
17
+ end
18
+ self.logger.error "resilient_insert_unhandled", error: e, logged_webhook_kwargs: kwargs
19
+ raise
20
+ end
21
+
22
+ def write_to(dburl, service_integration_opaque_id, str_payload)
23
+ tblname = Webhookdb::LoggedWebhook.resilient_table_name
24
+ Sequel.connect(dburl, single_threaded: true) do |db|
25
+ begin
26
+ db.create_table?(tblname.to_sym) do
27
+ primary_key :pk
28
+ text :service_integration_opaque_id
29
+ text :json_payload
30
+ end
31
+ rescue Sequel::UniqueConstraintViolation
32
+ # We cannot avoid this race condition. If needed, we can optimize this, but it's a pain
33
+ # so don't worry about it for now.
34
+ nil
35
+ end
36
+ db.from(tblname).insert(service_integration_opaque_id:, json_payload: str_payload)
37
+ end
38
+ return true
39
+ rescue StandardError => e
40
+ self.logger.debug "resilient_insert_failure", error: e, **self._dburl_log_kwargs(dburl)
41
+ return false
42
+ end
43
+
44
+ def _dburl_log_kwargs(dburl)
45
+ u = URI(dburl)
46
+ return {fallback_database_host: u.host, fallback_database_name: u.path}
47
+ end
48
+
49
+ # - For each (reachable) database:
50
+ # - Select 1 row, with a lock
51
+ # - Replay the webhook
52
+ # - On success, delete the row
53
+ # - On failure, process the next row
54
+ def replay
55
+ tblname = Webhookdb::LoggedWebhook.resilient_table_name
56
+ begin
57
+ Webhookdb::LoggedWebhook.db.execute("SELECT 1=1")
58
+ rescue Sequel::DatabaseError
59
+ self.logger.debug("resilient_replay_primary_db_not_ready")
60
+ return nil
61
+ end
62
+ replayed = 0
63
+ self.database_urls.each do |url|
64
+ Sequel.connect(url) do |rdb|
65
+ has_more = true
66
+ # Keep track of the last pk we've replayed, so we can grab the next available one.
67
+ # Otherwise we can end up spinning on the same one, especially with other threads.
68
+ seen_pk = 0
69
+ while has_more
70
+ # Each row must be processed in a transaction
71
+ rdb.transaction do
72
+ row = rdb.from(tblname).where { pk > seen_pk }.for_update.skip_locked.order(:pk).limit(1).first
73
+ if row.nil?
74
+ has_more = false
75
+ break # The break only works for the transaction
76
+ end
77
+ pk = row.fetch(:pk)
78
+ seen_pk = pk
79
+ payload = JSON.parse(row.fetch(:json_payload))
80
+ # We replay the webhook from a separate job
81
+ # so it can be done idempotently/exclusively.
82
+ lwh = Webhookdb::LoggedWebhook.create(payload)
83
+ lwh.replay_async
84
+ replayed += 1
85
+ rdb.from(tblname).where(pk:).delete
86
+ end
87
+ end
88
+ end
89
+ return replayed
90
+ rescue Sequel::DatabaseError
91
+ self.logger.debug("resilient_replay_fallback_unavailable", **self._dburl_log_kwargs(url))
92
+ return nil
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+
5
+ require "webhookdb/postgres/model"
6
+
7
+ class Webhookdb::LoggedWebhook < Webhookdb::Postgres::Model(:logged_webhooks)
8
+ include Appydays::Configurable
9
+
10
+ class << self
11
+ attr_accessor :available_resilient_database_urls
12
+ end
13
+
14
+ configurable(:logged_webhooks) do
15
+ # Space-separated URLs to use for resilient/high availability writes.
16
+ setting :resilient_database_urls, [], convert: ->(s) { s.split.map(&:strip) }
17
+ setting :resilient_database_env_vars, [], convert: ->(s) { s.split.map(&:strip) }
18
+ setting :resilient_table_name, "_logged_webhooks_resilient_writes"
19
+ # Using /replay can send this many webhooks in one request.
20
+ # Making this too high can cause timeouts- the caller may have to make multiple calls instead.
21
+ setting :maximum_replay_interval_hours, 4
22
+ # Webhooks this old cannot be replayed; they are probably truncated anyway.
23
+ setting :maximum_replay_history_hours, 7 * 24
24
+
25
+ after_configured do
26
+ self.available_resilient_database_urls = self.resilient_database_urls.dup
27
+ self.available_resilient_database_urls.concat(self.resilient_database_env_vars.map { |e| ENV.fetch(e, nil) })
28
+ end
29
+ end
30
+
31
+ many_to_one :organization, class: "Webhookdb::Organization"
32
+
33
+ many_to_one :service_integration,
34
+ class: "Webhookdb::ServiceIntegration",
35
+ key: :service_integration_opaque_id,
36
+ primary_key: :opaque_id
37
+
38
+ DELETE_UNOWNED = 14.days
39
+ DELETE_SUCCESSES = 90.days
40
+ TRUNCATE_SUCCESSES = 7.days
41
+ DELETE_FAILURES = 90.days
42
+ TRUNCATE_FAILURES = 30.days
43
+ # When we retry a request, set this so we know not to re-log it.
44
+ RETRY_HEADER = "Whdb-Logged-Webhook-Retry"
45
+ # When we retry a request, these headers
46
+ # must come from the Ruby client, NOT the original request.
47
+ NONOVERRIDABLE_HEADERS = [
48
+ "Accept-Encoding",
49
+ "Accept",
50
+ "Host",
51
+ "Version",
52
+ ].to_set
53
+ # These headers have been added by Heroku/our web host,
54
+ # so should not be part of the retry.
55
+ WEBHOST_HEADERS = [
56
+ "Connection",
57
+ "Connect-Time",
58
+ "X-Request-Id",
59
+ "X-Forwarded-For",
60
+ "X-Request-Start",
61
+ "Total-Route-Time",
62
+ "X-Forwarded-Port",
63
+ "X-Forwarded-Proto",
64
+ "Via",
65
+ ].to_set
66
+
67
+ # Trim logged webhooks to keep this table to a reasonable size.
68
+ # The current trim algorithm and rationale is:
69
+ #
70
+ # - Logs that belong to inserts that were not part of an org are for our internal use only.
71
+ # They usually indicate an integration that was misconfigured, or is for an org that doesn't exist.
72
+ # We keep these around for 2 weeks (they are always errors since they have no org).
73
+ # Ideally we investigate and remove them before that.
74
+ # We may need to 'block' certain opaque ids from being logged in the future,
75
+ # if for example we cannot get a client to turn off a misconfigured webhook.
76
+ # - Successful webhooks get their contents (request body and headers)
77
+ # _truncated_ after 7 days (but the webhook row remains).
78
+ # Usually we don't need to worry about these so in theory we can avoid logging verbose info at all.
79
+ # - Successful webhooks are deleted entirely after 90 days.
80
+ # Truncated webhooks are useful for statistics,
81
+ # but we can remove them earlier in the future.
82
+ # - Failed webhooks get their contents truncated after 30 days,
83
+ # but the webhook row remains. We have a longer truncation date
84
+ # so we have more time to investigate.
85
+ # - Error webhooks are deleted entirely after 90 days.
86
+ def self.trim(now: Time.now)
87
+ owned = self.exclude(organization_id: nil)
88
+ unowned = self.where(organization_id: nil)
89
+ successes = owned.where { response_status < 400 }
90
+ failures = owned.where { response_status >= 400 }
91
+ # Delete old unowned
92
+ unowned.where { inserted_at < now - DELETE_UNOWNED }.delete
93
+ # Delete successes first so they don't have to be truncated
94
+ successes.where { inserted_at < now - DELETE_SUCCESSES }.delete
95
+ self.truncate_dataset(successes.where { inserted_at < now - TRUNCATE_SUCCESSES })
96
+ # Delete failures
97
+ failures.where { inserted_at < now - DELETE_FAILURES }.delete
98
+ self.truncate_dataset(failures.where { inserted_at < now - TRUNCATE_FAILURES })
99
+ end
100
+
101
+ # Send instances back in 'through the front door' of this API.
102
+ # Return is a partition of [logs with 2xx responses, others].
103
+ # Generally you can safely call `truncate_logs(result[0])`,
104
+ # or pass in (truncate_successful: true).
105
+ def self.retry_logs(instances, truncate_successful: false)
106
+ successes, failures = instances.partition do |lw|
107
+ uri = URI(Webhookdb.api_url + lw.request_path)
108
+ req = Net::HTTP::Post.new(uri.path, {"Content-Type" => "application/json"})
109
+ req.body = lw.request_body
110
+ # This is going to have these headers:
111
+ # ["content-type", "accept-encoding", "accept", "user-agent", "host"]
112
+ # We want to keep all of these, except if user-agent or content-type were set
113
+ # in the original request; then we want to use those.
114
+ # Additionally, there are a whole set of headers we'll find on our webserver
115
+ # that are added by our web platform, which we do NOT want to include.
116
+ lw.request_headers.each do |k, v|
117
+ next if Webhookdb::LoggedWebhook::WEBHOST_HEADERS.include?(k)
118
+ next if Webhookdb::LoggedWebhook::NONOVERRIDABLE_HEADERS.include?(k)
119
+ req[k] = v
120
+ end
121
+ req[Webhookdb::LoggedWebhook::RETRY_HEADER] = lw.id
122
+ resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
123
+ http.request(req)
124
+ end
125
+ resp.code.to_i < 400
126
+ end
127
+ self.truncate_logs(*successes) if truncate_successful
128
+ return successes, failures
129
+ end
130
+
131
+ def retry_one(truncate_successful: false)
132
+ _, bad = self.class.retry_logs([self], truncate_successful:)
133
+ return bad.empty?
134
+ end
135
+
136
+ def replay_async
137
+ return self.publish_immediate("replay", self.id)
138
+ end
139
+
140
+ # Truncate the logs id'ed by the given instances.
141
+ # Instances are NOT modified; you need to .refresh to see truncated values.
142
+ def self.truncate_logs(*instances)
143
+ ds = self.where(id: instances.map(&:id))
144
+ return self.truncate_dataset(ds)
145
+ end
146
+
147
+ def self.truncate_dataset(ds)
148
+ return ds.update(request_body: "", request_headers: "{}", truncated_at: Time.now)
149
+ end
150
+
151
+ def truncated?
152
+ return self.truncated_at ? true : false
153
+ end
154
+
155
+ # Insert the logged webhook, and fall back to inserting into the configured
156
+ # available_resilient_database_urls. If none are inserted successfully, raise the error;
157
+ # otherwise, swallow the insert error and more on.
158
+ #
159
+ # Note that these resilient inserts are MUCH slower than normal inserts;
160
+ # they require a separate database connection, CREATE TABLE call, etc.
161
+ # But it's a reasonable way to handle when the database is down.
162
+ def self.resilient_insert(**kwargs)
163
+ Resilient.new.insert(kwargs)
164
+ end
165
+
166
+ # Replay and delete all rows in the resilient database tables.
167
+ def self.resilient_replay
168
+ Resilient.new.replay
169
+ end
170
+ end
171
+
172
+ require "webhookdb/logged_webhook/resilient"
173
+
174
+ # Table: logged_webhooks
175
+ # ---------------------------------------------------------------------------------------------------------------------------
176
+ # Columns:
177
+ # id | bigint | PRIMARY KEY DEFAULT nextval('logged_webhooks_id_seq'::regclass)
178
+ # inserted_at | timestamp with time zone | NOT NULL DEFAULT now()
179
+ # truncated_at | timestamp with time zone |
180
+ # request_body | text | NOT NULL
181
+ # request_headers | jsonb | NOT NULL
182
+ # response_status | smallint | NOT NULL
183
+ # service_integration_opaque_id | text | NOT NULL
184
+ # organization_id | integer |
185
+ # request_method | text | NOT NULL
186
+ # request_path | text | NOT NULL
187
+ # Indexes:
188
+ # logged_webhooks_pkey | PRIMARY KEY btree (id)
189
+ # logged_webhooks_inserted_at_index | btree (inserted_at)
190
+ # logged_webhooks_organization_id_index | btree (organization_id)
191
+ # logged_webhooks_service_integration_opaque_id_index | btree (service_integration_opaque_id)
192
+ # Foreign key constraints:
193
+ # logged_webhooks_organization_id_fkey | (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
194
+ # ---------------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/postgres/model"
4
+
5
+ require "webhookdb/message"
6
+
7
+ class Webhookdb::Message::Body < Webhookdb::Postgres::Model(:message_bodies)
8
+ plugin :timestamps
9
+
10
+ many_to_one :delivery, class: "Webhookdb::Message::Delivery"
11
+ end
12
+
13
+ # Table: message_bodies
14
+ # ----------------------------------------------------------------------------------------------------
15
+ # Columns:
16
+ # id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
17
+ # content | text | NOT NULL
18
+ # mediatype | text | NOT NULL
19
+ # delivery_id | integer | NOT NULL
20
+ # Indexes:
21
+ # message_bodies_pkey | PRIMARY KEY btree (id)
22
+ # message_bodies_delivery_id_index | btree (delivery_id)
23
+ # Foreign key constraints:
24
+ # message_bodies_delivery_id_fkey | (delivery_id) REFERENCES message_deliveries(id) ON DELETE CASCADE
25
+ # ----------------------------------------------------------------------------------------------------