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,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/replicator/transistor_v1_mixin"
4
+
5
+ class Webhookdb::Replicator::TransistorShowV1 < Webhookdb::Replicator::Base
6
+ include Appydays::Loggable
7
+ include Webhookdb::Replicator::TransistorV1Mixin
8
+
9
+ # @return [Webhookdb::Replicator::Descriptor]
10
+ def self.descriptor
11
+ return Webhookdb::Replicator::Descriptor.new(
12
+ name: "transistor_show_v1",
13
+ ctor: ->(sint) { Webhookdb::Replicator::TransistorShowV1.new(sint) },
14
+ feature_roles: [],
15
+ resource_name_singular: "Transistor Show",
16
+ supports_backfill: true,
17
+ api_docs_url: "https://developers.transistor.fm/#Show",
18
+ )
19
+ end
20
+
21
+ def _denormalized_columns
22
+ return [
23
+ Webhookdb::Replicator::Column.new(:author, TEXT, data_key: ["attributes", "author"]),
24
+ Webhookdb::Replicator::Column.new(
25
+ :created_at, TIMESTAMP,
26
+ index: true,
27
+ data_key: ["attributes", "created_at"],
28
+ ),
29
+ Webhookdb::Replicator::Column.new(:description, TEXT, data_key: ["attributes", "description"]),
30
+ Webhookdb::Replicator::Column.new(:title, TEXT, data_key: ["attributes", "title"]),
31
+ Webhookdb::Replicator::Column.new(
32
+ :updated_at,
33
+ TIMESTAMP,
34
+ index: true,
35
+ data_key: ["attributes", "updated_at"],
36
+ ),
37
+ Webhookdb::Replicator::Column.new(:website, TEXT, data_key: ["attributes", "website"]),
38
+ ]
39
+ end
40
+
41
+ def _fetch_backfill_page(pagination_token, **_kwargs)
42
+ pagination_token ||= [false, 1]
43
+ iterating_over_private, page = pagination_token
44
+
45
+ url = "https://api.transistor.fm/v1/shows"
46
+
47
+ response = Webhookdb::Http.get(
48
+ url,
49
+ headers: {"x-api-key" => self.service_integration.backfill_key},
50
+ body: {pagination: {page:}, private: iterating_over_private},
51
+ logger: self.logger,
52
+ timeout: Webhookdb::Transistor.http_timeout,
53
+ )
54
+ data = response.parsed_response
55
+ current_page = data["meta"]["currentPage"]
56
+ total_pages = data["meta"]["totalPages"]
57
+ shows = data["data"]
58
+
59
+ if current_page < total_pages
60
+ # If we still have pages on this list, go to the next one
61
+ return shows, [iterating_over_private, current_page + 1]
62
+ end
63
+ # If we are done with the public list, we can now iterate over private shows
64
+ return shows, [true, 1] unless iterating_over_private
65
+ # Otherwise we are on the last page of our private list
66
+ return shows, nil
67
+ end
68
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Webhookdb::Replicator::TransistorV1Mixin
4
+ include Webhookdb::DBAdapter::ColumnTypes
5
+
6
+ def _webhook_response(_request)
7
+ # As of 9/15/21 there is no way to verify authenticity of these webhooks
8
+ return Webhookdb::WebhookResponse.ok
9
+ end
10
+
11
+ def _remote_key_column
12
+ return Webhookdb::Replicator::Column.new(:transistor_id, TEXT, data_key: "id")
13
+ end
14
+
15
+ def _timestamp_column_name
16
+ return :updated_at
17
+ end
18
+
19
+ def _resource_and_event(request)
20
+ body = request.body
21
+ return body["data"], body if body.key?("data")
22
+ return body, nil
23
+ end
24
+
25
+ def _update_where_expr
26
+ return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
27
+ end
28
+
29
+ def calculate_backfill_state_machine
30
+ step = Webhookdb::Replicator::StateMachineStep.new
31
+ if self.service_integration.backfill_key.blank?
32
+ step.output = %(Great! We've created your #{self.resource_name_plural} integration.
33
+
34
+ Transistor does not support #{self.resource_name_singular} webhooks, so to fill your database,
35
+ we need to use the API to make requests, which requires your API Key.
36
+
37
+ From your Transistor dashboard, go to the "Your Account" page,
38
+ at https://dashboard.transistor.fm/account
39
+ On the left side of the bottom of the page you should be able to see your API key.
40
+
41
+ Copy that API key.
42
+ )
43
+ return step.secret_prompt("API Key").backfill_key(self.service_integration)
44
+ end
45
+
46
+ unless (result = self.verify_backfill_credentials).verified
47
+ self.service_integration.replicator.clear_backfill_information
48
+ step.output = result.message
49
+ return step.secret_prompt("API Key").backfill_key(self.service_integration)
50
+ end
51
+
52
+ step.output = %(Great! We are going to start replicating your #{self.resource_name_plural}.
53
+ #{self._query_help_output}
54
+ )
55
+ return step.completed
56
+ end
57
+
58
+ def _verify_backfill_401_err_msg
59
+ return "It looks like that API Key is invalid. Please reenter the API Key you just created:"
60
+ end
61
+
62
+ def _verify_backfill_err_msg
63
+ return "An error occurred. Please reenter the API Key you just created:"
64
+ end
65
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/twilio"
4
+
5
+ class Webhookdb::Replicator::TwilioSmsV1 < Webhookdb::Replicator::Base
6
+ include Appydays::Loggable
7
+
8
+ # @return [Webhookdb::Replicator::Descriptor]
9
+ def self.descriptor
10
+ return Webhookdb::Replicator::Descriptor.new(
11
+ name: "twilio_sms_v1",
12
+ ctor: ->(sint) { Webhookdb::Replicator::TwilioSmsV1.new(sint) },
13
+ feature_roles: [],
14
+ resource_name_singular: "Twilio SMS Message",
15
+ supports_backfill: true,
16
+ api_docs_url: "https://www.twilio.com/docs/usage/api#send-an-sms-with-twilios-api",
17
+ )
18
+ end
19
+
20
+ def _webhook_response(request)
21
+ auth = request.get_header("Authorization")
22
+ if auth.nil? || !auth.match(/^Basic /)
23
+ return Webhookdb::WebhookResponse.new(
24
+ status: 401,
25
+ body: "",
26
+ reason: "challenge",
27
+ headers: {"Content-Type" => "text/plain", "WWW-Authenticate" => 'Basic realm="Webhookdb"'},
28
+ )
29
+ end
30
+ user_and_pass = Base64.decode64(auth.gsub(/^Basic /, ""))
31
+ if user_and_pass != self.service_integration.webhook_secret
32
+ return Webhookdb::WebhookResponse.new(
33
+ status: 401,
34
+ body: "",
35
+ reason: "invalid",
36
+ headers: {"Content-Type" => "text/plain"},
37
+ )
38
+ end
39
+ return Webhookdb::WebhookResponse.new(
40
+ status: 202,
41
+ headers: {"Content-Type" => "text/xml"},
42
+ body: "<Response></Response>",
43
+ )
44
+ end
45
+
46
+ def calculate_backfill_state_machine
47
+ step = Webhookdb::Replicator::StateMachineStep.new
48
+ unless self.service_integration.backfill_key.present?
49
+ step.needs_input = true
50
+ step.output = %(Great! We've created your Twilio SMS integration.
51
+
52
+ Rather than using your Twilio Webhooks (of which each number can have only one),
53
+ we poll Twilio for changes, and will also backfill historical SMS.
54
+
55
+ To do this, we need your Account SID and Auth Token.
56
+ Both of these values should be visible from the homepage of your Twilio admin Dashboard.
57
+ )
58
+ return step.secret_prompt("Account SID").backfill_key(self.service_integration)
59
+ end
60
+
61
+ unless self.service_integration.backfill_secret.present?
62
+ return step.secret_prompt("Auth Token").backfill_secret(self.service_integration)
63
+ end
64
+
65
+ unless (result = self.verify_backfill_credentials).verified
66
+ self.service_integration.replicator.clear_backfill_information
67
+ step.output = result.message
68
+ return step.secret_prompt("API Key").backfill_key(self.service_integration)
69
+ end
70
+
71
+ step.output = %(We are going to start replicating your Twilio SMS information, and will keep it updated.
72
+ #{self._query_help_output}
73
+ )
74
+ return step.completed
75
+ end
76
+
77
+ def _verify_backfill_401_err_msg
78
+ return "It looks like that API Key is invalid. Please reenter the API Key you just created:"
79
+ end
80
+
81
+ def _verify_backfill_err_msg
82
+ return "An error occurred. Please reenter the API Key you just created:"
83
+ end
84
+
85
+ def _remote_key_column
86
+ return Webhookdb::Replicator::Column.new(:twilio_id, TEXT, data_key: "sid")
87
+ end
88
+
89
+ def _denormalized_columns
90
+ return [
91
+ Webhookdb::Replicator::Column.new(
92
+ :date_created,
93
+ TIMESTAMP,
94
+ index: true,
95
+ converter: Webhookdb::Replicator::Column::CONV_PARSE_TIME,
96
+ ),
97
+ Webhookdb::Replicator::Column.new(
98
+ :date_sent,
99
+ TIMESTAMP,
100
+ index: true,
101
+ converter: Webhookdb::Replicator::Column::CONV_PARSE_TIME,
102
+ ),
103
+ Webhookdb::Replicator::Column.new(
104
+ :date_updated,
105
+ TIMESTAMP,
106
+ index: true,
107
+ converter: Webhookdb::Replicator::Column::CONV_PARSE_TIME,
108
+ ),
109
+ Webhookdb::Replicator::Column.new(:direction, TEXT),
110
+ Webhookdb::Replicator::Column.new(:from, TEXT, index: true),
111
+ Webhookdb::Replicator::Column.new(:status, TEXT),
112
+ Webhookdb::Replicator::Column.new(:to, TEXT, index: true),
113
+ ]
114
+ end
115
+
116
+ def _timestamp_column_name
117
+ return :date_updated
118
+ end
119
+
120
+ def _resource_and_event(request)
121
+ return request.body, nil
122
+ end
123
+
124
+ def _update_where_expr
125
+ return self.qualified_table_sequel_identifier[:date_updated] < Sequel[:excluded][:date_updated]
126
+ end
127
+
128
+ def _fetch_backfill_page(pagination_token, last_backfilled:)
129
+ url = "https://api.twilio.com"
130
+ if pagination_token.blank?
131
+ date_send_max = Date.tomorrow
132
+ url += "/2010-04-01/Accounts/#{self.service_integration.backfill_key}/Messages.json" \
133
+ "?PageSize=100&DateSend%3C=#{date_send_max}"
134
+ else
135
+ url += pagination_token
136
+ end
137
+ response = Webhookdb::Http.get(
138
+ url,
139
+ basic_auth: {username: self.service_integration.backfill_key,
140
+ password: self.service_integration.backfill_secret,},
141
+ logger: self.logger,
142
+ timeout: Webhookdb::Twilio.http_timeout,
143
+ )
144
+ data = response.parsed_response
145
+ messages = data["messages"]
146
+
147
+ if last_backfilled.present?
148
+ earliest_data_created = messages.empty? ? Time.at(0) : messages[-1].fetch("date_created")
149
+ paged_to_already_seen_records = earliest_data_created < last_backfilled
150
+
151
+ return messages, nil if paged_to_already_seen_records
152
+ end
153
+
154
+ return messages, data["next_page_uri"]
155
+ end
156
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Webhookdb::Replicator::WebhookRequest < Webhookdb::TypedStruct
4
+ attr_accessor :body, :headers, :path, :method
5
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Webhookdb::Replicator::WebhookdbCustomerV1 < Webhookdb::Replicator::Base
4
+ include Appydays::Loggable
5
+
6
+ # @return [Webhookdb::Replicator::Descriptor]
7
+ def self.descriptor
8
+ return Webhookdb::Replicator::Descriptor.new(
9
+ name: "webhookdb_customer_v1",
10
+ ctor: ->(sint) { Webhookdb::Replicator::WebhookdbCustomerV1.new(sint) },
11
+ feature_roles: ["internal"],
12
+ resource_name_singular: "WebookDB Customer",
13
+ supports_webhooks: true,
14
+ )
15
+ end
16
+
17
+ def _webhook_response(request)
18
+ sek = request.env["HTTP_WHDB_SECRET"]
19
+ return Webhookdb::WebhookResponse.ok if sek == self.service_integration.webhook_secret
20
+ return Webhookdb::WebhookResponse.error("Whdb-Secret header is missing") if sek.nil?
21
+ return Webhookdb::WebhookResponse.error("Whdb-Secret value does not match configured secret")
22
+ end
23
+
24
+ def _timestamp_column_name
25
+ return :updated_at
26
+ end
27
+
28
+ def calculate_webhook_state_machine
29
+ step = Webhookdb::Replicator::StateMachineStep.new
30
+ self.service_integration.update(webhook_secret: Webhookdb::Id.rand_enc(16)) if
31
+ self.service_integration.webhook_secret.blank?
32
+ step.output = %(WebhookDB is now listening for changes to #{self.resource_name_plural}
33
+ and will replicate them into the table for this service integration.
34
+
35
+ Whenever a #{self.resource_name_singular} changes, a request will be sent to:
36
+
37
+ #{self._webhook_endpoint}
38
+
39
+ With the header:
40
+
41
+ Whdb-Secret: #{self.service_integration.webhook_secret}
42
+
43
+ Which will be received by this running instance, so there's nothing else you have to do.
44
+
45
+ #{self._query_help_output}
46
+ )
47
+ return step.completed
48
+ end
49
+
50
+ def _remote_key_column
51
+ return Webhookdb::Replicator::Column.new(:webhookdb_id, TEXT, data_key: "id")
52
+ end
53
+
54
+ def _denormalized_columns
55
+ return [
56
+ Webhookdb::Replicator::Column.new(:created_at, TIMESTAMP, index: true),
57
+ Webhookdb::Replicator::Column.new(:email, TEXT, index: true),
58
+ Webhookdb::Replicator::Column.new(
59
+ :updated_at,
60
+ TIMESTAMP,
61
+ index: true,
62
+ defaulter: Webhookdb::Replicator::Column.defaulter_from_resource_field(:created_at),
63
+ ),
64
+ ]
65
+ end
66
+
67
+ def _resource_and_event(request)
68
+ return request.body, nil
69
+ end
70
+
71
+ def _update_where_expr
72
+ return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
73
+ end
74
+ end
@@ -0,0 +1,224 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+ require "webhookdb/typed_struct"
5
+
6
+ class Webhookdb::Replicator
7
+ include Appydays::Configurable
8
+ extend Webhookdb::MethodUtilities
9
+
10
+ configurable(:replicator) do
11
+ setting :always_process_synchronously, false
12
+ end
13
+
14
+ PLUGIN_DIRNAME = "replicator_ext"
15
+ PLUGIN_DIR = Pathname(__FILE__).dirname + PLUGIN_DIRNAME
16
+
17
+ # Raised when there is no service registered for a name.
18
+ class Invalid < StandardError; end
19
+
20
+ # Raised when credentials to interact with a service are not set up.
21
+ # Usually this is due to a missing dependency.
22
+ class CredentialsMissing < StandardError; end
23
+
24
+ # Statically describe a replicator.
25
+ class Descriptor < Webhookdb::TypedStruct
26
+ # @!attribute name
27
+ # Name of the replicator, like 'stripe_charge_v1'.
28
+ # Appears externally in many places, so must be meaningful.
29
+ # @return [String]
30
+ attr_reader :name
31
+
32
+ # @!attribute ctor
33
+ # Method invoked with the +Webhookdb::ServiceIntegration+, and should return a new instance
34
+ # of the integration. Can also be an object that responds to `new`.
35
+ # @return [Proc]
36
+ attr_reader :ctor
37
+
38
+ # @!attribute feature_roles
39
+ # Used for feature flagging functionality.
40
+ # Usually will be [], but other possible values are:
41
+ # -'internal' e.g. our fake integration
42
+ # -'unreleased' for works in progress
43
+ # -'beta' if we don't want most people to have access
44
+ # @return [Array<String>]
45
+ attr_reader :feature_roles
46
+
47
+ # @!attribute resource_name_singular
48
+ # Name of the resource, like "Acme Sprocket"
49
+ # @return [String]
50
+ attr_reader :resource_name_singular
51
+
52
+ # @!attribute resource_name_plural
53
+ # Defaults to resource_name_singular+s.
54
+ # @return [String]
55
+ attr_reader :resource_name_plural
56
+
57
+ # @!attribute dependency_descriptor
58
+ # The descriptor for the service this one depends on (the parent).
59
+ # @return [Webhookdb::Replicator::Descriptor]
60
+ attr_reader :dependency_descriptor
61
+
62
+ # True if this integration supports webhooks (real-time or user-built webhook payloads).
63
+ attr_reader :supports_webhooks
64
+ # True if this integration supports user-driven backfilling,
65
+ # usually by paginating all resources.
66
+ attr_reader :supports_backfill
67
+
68
+ # If this integration has specific documentation,
69
+ # link its url here. It is used to build custom error messages,
70
+ # for example in case 'backfill' is called but not supported.
71
+ attr_reader :documentation_url
72
+
73
+ # Markdown description of this replicator.
74
+ attr_reader :description
75
+
76
+ # URL pointing to the provider (not-WebhookDB) docs for this integration.
77
+ attr_reader :api_docs_url
78
+
79
+ # Is this an enterprise-only replicator?
80
+ attr_reader :enterprise
81
+
82
+ def initialize(
83
+ name:,
84
+ ctor:,
85
+ resource_name_singular:,
86
+ feature_roles:,
87
+ supports_webhooks: false,
88
+ supports_backfill: false,
89
+ resource_name_plural: nil,
90
+ dependency_descriptor: nil,
91
+ api_docs_url: "",
92
+ description: nil,
93
+ enterprise: false,
94
+ documentation_url: nil
95
+ )
96
+ raise ArgumentError, "must support one or both of webhooks and backfill" unless
97
+ supports_webhooks || supports_backfill
98
+ super(
99
+ name:,
100
+ resource_name_singular:,
101
+ feature_roles:,
102
+ supports_webhooks:,
103
+ supports_backfill:,
104
+ dependency_descriptor:,
105
+ documentation_url:,
106
+ api_docs_url:,
107
+ enterprise:
108
+ )
109
+ @ctor = ctor.is_a?(Class) ? ctor.method(:new) : ctor
110
+ @resource_name_plural = resource_name_plural || "#{self.resource_name_singular}s"
111
+ @description = description || "Replicate #{self.resource_name_plural} into your database."
112
+ self.feature_roles
113
+ end
114
+
115
+ def inspect
116
+ return "#{self.class.name}(name: #{self.name})"
117
+ end
118
+
119
+ def ==(other)
120
+ return self.class == other.class &&
121
+ self.name == other.name &&
122
+ self.resource_name_singular == other.resource_name_singular
123
+ end
124
+
125
+ alias supports_webhooks? supports_webhooks
126
+ alias supports_backfill? supports_backfill
127
+ alias enterprise? enterprise
128
+ def webhooks_only? = self.supports_webhooks? && !self.supports_backfill?
129
+ def backfill_only? = !self.supports_webhooks? && self.supports_backfill?
130
+ def supports_webhooks_and_backfill? = self.supports_webhooks? && self.supports_backfill?
131
+ end
132
+
133
+ class IndexSpec < Webhookdb::TypedStruct
134
+ attr_reader :columns, :where
135
+ end
136
+
137
+ class << self
138
+ # @return [Hash{String => Webhookdb::Replicator::Descriptor}]
139
+ def registry
140
+ return @registry ||= {}
141
+ end
142
+
143
+ def register(cls)
144
+ desc = cls.descriptor
145
+ raise TypeError, "descriptor must be a Descriptor, got #{desc.class.name}" unless desc.is_a?(Descriptor)
146
+ self.registry[desc.name] = desc
147
+ end
148
+
149
+ # Return a new replicator for the given integration.
150
+ #
151
+ # @param service_integration [Webhookdb::ServiceIntegration]
152
+ # @return [Webhookdb::Replicator::Base]
153
+ def create(service_integration)
154
+ name = service_integration.service_name
155
+ descr = self.registered!(name)
156
+ return descr.ctor.call(service_integration)
157
+ end
158
+
159
+ # Returns the service with the given name, or +nil+ if none is registered.
160
+ # @return [Webhookdb::Replicator::Descriptor]
161
+ def registered(name)
162
+ return @registry[name]
163
+ end
164
+
165
+ # @raise [Webhookdb::Replicator::Invalid] When the name is invalid.
166
+ # @return [Webhookdb::Replicator::Descriptor]
167
+ def registered!(name)
168
+ raise ArgumentError, "name cannot be blank" if name.blank?
169
+ r = self.registered(name)
170
+ return r if r
171
+ raise Invalid, name
172
+ end
173
+
174
+ def load_replicators
175
+ existing_descendants = Webhookdb::Replicator::Base.descendants
176
+ ["replicator", PLUGIN_DIRNAME].each do |d|
177
+ Gem.find_files(File.join("webhookdb/#{d}/*.rb")).each do |path|
178
+ next if path.include?("/spec/")
179
+ require path
180
+ end
181
+ end
182
+ new_descendants = Webhookdb::Replicator::Base.descendants
183
+ newly_registered = new_descendants - existing_descendants
184
+ newly_registered.each { |cls| self.register(cls) }
185
+ return newly_registered
186
+ end
187
+
188
+ def _require_files(dir)
189
+ splitter = "webhookdb/" + dir.to_s.rpartition("/").last
190
+ dir.glob("*.rb").each do |path|
191
+ base = path.basename.to_s[..-4]
192
+ require("#{splitter}/#{base}")
193
+ end
194
+ end
195
+
196
+ # @param sint [Webhookdb::ServiceIntegration]
197
+ # @return [Webhookdb::ServiceIntegration,nil]
198
+ def find_root(sint)
199
+ max_depth = 15
200
+ parent = sint.depends_on
201
+ return sint if parent.nil?
202
+ max_depth.times do
203
+ return parent if parent.depends_on.nil?
204
+ parent = parent.depends_on
205
+ end
206
+ return nil
207
+ end
208
+
209
+ # @param sint [Webhookdb::ServiceIntegration]
210
+ # @return [Webhookdb::ServiceIntegration]
211
+ def find_at_root!(sint, service_name:)
212
+ root = self.find_root(sint)
213
+ bad_auth = root&.service_name != service_name
214
+ raise self::CredentialsMissing, "Could not find root integration for #{sint.inspect}" if bad_auth
215
+ return root
216
+ end
217
+ end
218
+
219
+ require "webhookdb/replicator/state_machine_step"
220
+ require "webhookdb/replicator/column"
221
+ require "webhookdb/replicator/base"
222
+ require "webhookdb/replicator/docgen"
223
+ load_replicators
224
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/postgres/model"
4
+
5
+ class Webhookdb::Role < Webhookdb::Postgres::Model(:roles)
6
+ # n.b. Because of the uniqueness constraint on "name", there is only one "admin" role. Its meaning
7
+ # depends on the context: if the customer has this role, they are an admin; if the org membership has
8
+ # this role, the customer is an org admin.
9
+ def self.admin_role
10
+ return Webhookdb.cached_get("role_admin") do
11
+ self.find_or_create_or_find(name: "admin")
12
+ end
13
+ end
14
+
15
+ def self.non_admin_role
16
+ return Webhookdb.cached_get("role_member") do
17
+ self.find_or_create_or_find(name: "member")
18
+ end
19
+ end
20
+
21
+ # used to indicate user status within the org, e.g. whether user is an org admin & can create services
22
+ one_to_many :organization_memberships, class: "Webhookdb::OrganizationMembership"
23
+
24
+ # used to indicate user status within the app itself, i.e. whether user is an app admin
25
+ many_to_many :customers,
26
+ class: "Webhookdb::Customer",
27
+ join_table: :roles_customers
28
+ end
29
+
30
+ # Table: roles
31
+ # ---------------------------------------------------------------------------------------------------------------------------
32
+ # Columns:
33
+ # id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
34
+ # name | text | NOT NULL
35
+ # Indexes:
36
+ # roles_pkey | PRIMARY KEY btree (id)
37
+ # roles_name_key | UNIQUE btree (name)
38
+ # Referenced By:
39
+ # feature_roles_organizations | feature_roles_organizations_role_id_fkey | (role_id) REFERENCES roles(id)
40
+ # organization_memberships | organization_memberships_membership_role_id_fkey | (membership_role_id) REFERENCES roles(id)
41
+ # roles_customers | roles_customers_role_id_fkey | (role_id) REFERENCES roles(id)
42
+ # ---------------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sentry-ruby"
4
+ require "appydays/configurable"
5
+ require "appydays/loggable"
6
+
7
+ require "webhookdb"
8
+
9
+ module Webhookdb::Sentry
10
+ include Appydays::Configurable
11
+ include Appydays::Loggable
12
+
13
+ configurable(:sentry) do
14
+ setting :dsn, ""
15
+
16
+ # Apply the current configuration to Sentry.
17
+ # See https://docs.sentry.io/clients/ruby/config/ for more info.
18
+ after_configured do
19
+ if self.dsn
20
+ # See https://github.com/getsentry/sentry-ruby/issues/1756
21
+ require "sentry-sidekiq"
22
+ Sentry.init do |config|
23
+ config.dsn = dsn
24
+ config.logger = self.logger
25
+ end
26
+ else
27
+ Sentry.instance_variable_set(:@main_hub, nil)
28
+ end
29
+ end
30
+ end
31
+
32
+ def self.enabled?
33
+ return self.dsn.present?
34
+ end
35
+ end