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,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/increase"
4
+ require "webhookdb/replicator/increase_v1_mixin"
5
+
6
+ class Webhookdb::Replicator::IncreaseLimitV1 < Webhookdb::Replicator::Base
7
+ include Appydays::Loggable
8
+ include Webhookdb::Replicator::IncreaseV1Mixin
9
+
10
+ # @return [Webhookdb::Replicator::Descriptor]
11
+ def self.descriptor
12
+ return Webhookdb::Replicator::Descriptor.new(
13
+ name: "increase_limit_v1",
14
+ ctor: ->(sint) { Webhookdb::Replicator::IncreaseLimitV1.new(sint) },
15
+ feature_roles: [],
16
+ resource_name_singular: "Increase Limit",
17
+ supports_webhooks: true,
18
+ supports_backfill: true,
19
+ api_docs_url: "https://increase.com/documentation/api",
20
+ )
21
+ end
22
+
23
+ def _remote_key_column
24
+ return Webhookdb::Replicator::Column.new(:increase_id, TEXT, data_key: "id")
25
+ end
26
+
27
+ def _denormalized_columns
28
+ return [
29
+ Webhookdb::Replicator::Column.new(:interval, TEXT),
30
+ Webhookdb::Replicator::Column.new(:metric, TEXT),
31
+ Webhookdb::Replicator::Column.new(:model_id, TEXT, index: true),
32
+ Webhookdb::Replicator::Column.new(:model_type, TEXT),
33
+ Webhookdb::Replicator::Column.new(
34
+ :row_created_at,
35
+ TIMESTAMP,
36
+ data_key: "created_at",
37
+ event_key: "created_at",
38
+ defaulter: :now,
39
+ optional: true,
40
+ index: true,
41
+ ),
42
+ Webhookdb::Replicator::Column.new(
43
+ :row_updated_at,
44
+ TIMESTAMP,
45
+ data_key: "created_at",
46
+ event_key: "created_at",
47
+ defaulter: :now,
48
+ optional: true,
49
+ index: true,
50
+ ),
51
+ Webhookdb::Replicator::Column.new(:status, TEXT),
52
+ Webhookdb::Replicator::Column.new(:value, INTEGER),
53
+ ]
54
+ end
55
+
56
+ def _timestamp_column_name
57
+ return :row_updated_at
58
+ end
59
+
60
+ def _update_where_expr
61
+ return self.qualified_table_sequel_identifier[:row_updated_at] < Sequel[:excluded][:row_updated_at]
62
+ end
63
+
64
+ def _resource_and_event(request)
65
+ return self._find_resource_and_event(request.body, "limit")
66
+ end
67
+
68
+ def _upsert_update_expr(inserting, **_kwargs)
69
+ update = super
70
+ # Only set created_at if it's not set so the initial insert isn't modified.
71
+ self._coalesce_excluded_on_update(update, [:row_created_at])
72
+ return update
73
+ end
74
+
75
+ def _mixin_backfill_url
76
+ return "#{self.service_integration.api_url}/limits"
77
+ end
78
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/increase"
4
+ require "webhookdb/replicator/increase_v1_mixin"
5
+
6
+ class Webhookdb::Replicator::IncreaseTransactionV1 < Webhookdb::Replicator::Base
7
+ include Appydays::Loggable
8
+ include Webhookdb::Replicator::IncreaseV1Mixin
9
+
10
+ # @return [Webhookdb::Replicator::Descriptor]
11
+ def self.descriptor
12
+ return Webhookdb::Replicator::Descriptor.new(
13
+ name: "increase_transaction_v1",
14
+ ctor: ->(sint) { Webhookdb::Replicator::IncreaseTransactionV1.new(sint) },
15
+ feature_roles: [],
16
+ resource_name_singular: "Increase Transaction",
17
+ supports_webhooks: true,
18
+ supports_backfill: true,
19
+ api_docs_url: "https://increase.com/documentation/api",
20
+ )
21
+ end
22
+
23
+ def _remote_key_column
24
+ return Webhookdb::Replicator::Column.new(:increase_id, TEXT, data_key: "id")
25
+ end
26
+
27
+ def _denormalized_columns
28
+ return [
29
+ Webhookdb::Replicator::Column.new(:account_id, TEXT, index: true),
30
+ Webhookdb::Replicator::Column.new(:amount, INTEGER, index: true),
31
+ Webhookdb::Replicator::Column.new(
32
+ :created_at,
33
+ TIMESTAMP,
34
+ data_key: "created_at",
35
+ optional: true,
36
+ index: true,
37
+ ),
38
+ # date is a legacy field that is not documented in the API,
39
+ # but is still sent with transactions as of April 2022.
40
+ # We need to support the v1 schema, but do not want to depend
41
+ # on Increase continuing to send a transaction resource 'date' field.
42
+ Webhookdb::Replicator::Column.new(
43
+ :date,
44
+ DATE,
45
+ index: true,
46
+ data_key: "created_at",
47
+ optional: true,
48
+ converter: Webhookdb::Replicator::Column::CONV_TO_UTC_DATE,
49
+ ),
50
+ Webhookdb::Replicator::Column.new(:route_id, TEXT, index: true),
51
+ Webhookdb::Replicator::Column.new(
52
+ :updated_at,
53
+ TIMESTAMP,
54
+ data_key: "created_at",
55
+ event_key: "created_at",
56
+ defaulter: :now,
57
+ optional: true,
58
+ index: true,
59
+ ),
60
+ ]
61
+ end
62
+
63
+ def _update_where_expr
64
+ return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
65
+ end
66
+
67
+ def _resource_and_event(request)
68
+ return self._find_resource_and_event(request.body, "transaction")
69
+ end
70
+
71
+ def _mixin_backfill_url
72
+ return "#{self.service_integration.api_url}/transactions"
73
+ end
74
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/increase"
4
+
5
+ module Webhookdb::Replicator::IncreaseV1Mixin
6
+ def _mixin_backfill_url
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def _webhook_response(request)
11
+ return Webhookdb::Increase.webhook_response(request, self.service_integration.webhook_secret)
12
+ end
13
+
14
+ def _timestamp_column_name
15
+ return :updated_at
16
+ end
17
+
18
+ def _find_resource_and_event(body, desired_object_name)
19
+ return nil unless Webhookdb::Increase.contains_desired_object(body, desired_object_name)
20
+ return body.fetch("data"), body if body.key?("event") && body.key?("event_id")
21
+ return body, nil
22
+ end
23
+
24
+ def process_state_change(field, value)
25
+ # special handling for having a default value for api url
26
+ value = "https://api.increase.com" if field == "api_url" && value == ""
27
+ return super(field, value)
28
+ end
29
+
30
+ def calculate_webhook_state_machine
31
+ step = Webhookdb::Replicator::StateMachineStep.new
32
+ # if the service integration doesn't exist, create it with some standard values
33
+ unless self.service_integration.webhook_secret.present?
34
+ step.output = %(You are about to start replicating #{self.resource_name_plural} info into WebhookDB.
35
+ We've made an endpoint available for #{self.resource_name_singular} webhooks:
36
+
37
+ #{self._webhook_endpoint}
38
+
39
+ From your Increase admin dashboard, go to Applications -> Create Webhook.
40
+ In the "Webhook endpoint URL" field you can enter the URL above.
41
+ For the shared secret, you'll have to generate a strong password
42
+ (you can use '#{Webhookdb::Id.rand_enc(16)}')
43
+ and then enter it into the textbox.
44
+
45
+ Copy that shared secret value.
46
+ )
47
+ return step.secret_prompt("secret").webhook_secret(self.service_integration)
48
+ end
49
+
50
+ step.output = %(Great! WebhookDB is now listening for #{self.resource_name_singular} webhooks.
51
+ #{self._query_help_output}
52
+ In order to backfill existing #{self.resource_name_plural}, run this from a shell:
53
+
54
+ #{self._backfill_command}
55
+ )
56
+ return step.completed
57
+ end
58
+
59
+ def calculate_backfill_state_machine
60
+ step = Webhookdb::Replicator::StateMachineStep.new
61
+ unless self.service_integration.backfill_key.present?
62
+ step.output = %(In order to backfill #{self.resource_name_plural}, we need an API key.
63
+ From your Increase admin dashboard, go to Settings -> Development -> API Keys.
64
+ We'll need the Production key--copy that value to your clipboard.
65
+ )
66
+ return step.secret_prompt("API Key").backfill_key(self.service_integration)
67
+ end
68
+
69
+ unless self.service_integration.api_url.present?
70
+ step.output = %(Great. Now we want to make sure we're sending API requests to the right place.
71
+ For Increase, the API url is different when you are in Sandbox mode and when you are in Production mode.
72
+ For Sandbox mode, the API root url is:
73
+
74
+ https://sandbox.increase.com
75
+
76
+ For Production mode, which is our default, it is:
77
+
78
+ https://api.increase.com
79
+
80
+ Leave blank to use the default or paste the answer into this prompt.
81
+ )
82
+ return step.prompting("API url").api_url(self.service_integration)
83
+ end
84
+
85
+ unless (result = self.verify_backfill_credentials).verified
86
+ self.service_integration.replicator.clear_backfill_information
87
+ step.output = result.message
88
+ return step.secret_prompt("API Key").backfill_key(self.service_integration)
89
+ end
90
+
91
+ step.needs_input = false
92
+ step.output = %(Great! We are going to start backfilling your #{self.resource_name_plural}.
93
+ #{self._query_help_output}
94
+ )
95
+ step.complete = true
96
+ return step
97
+ end
98
+
99
+ def _verify_backfill_401_err_msg
100
+ return "It looks like that API Key is invalid. Please reenter the API Key you just created:"
101
+ end
102
+
103
+ def _verify_backfill_err_msg
104
+ return "An error occurred. Please reenter the API Key you just created:"
105
+ end
106
+
107
+ def _fetch_backfill_page(pagination_token, **_kwargs)
108
+ query = {}
109
+ (query[:cursor] = pagination_token) if pagination_token.present?
110
+ response = Webhookdb::Http.get(
111
+ self._mixin_backfill_url,
112
+ query,
113
+ headers: {"Authorization" => ("Bearer " + self.service_integration.backfill_key)},
114
+ logger: self.logger,
115
+ timeout: Webhookdb::Increase.http_timeout,
116
+ )
117
+ data = response.parsed_response
118
+ next_page_param = data.dig("response_metadata", "next_cursor")
119
+ return data["data"], next_page_param
120
+ end
121
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/increase"
4
+ require "webhookdb/replicator/increase_v1_mixin"
5
+
6
+ class Webhookdb::Replicator::IncreaseWireTransferV1 < Webhookdb::Replicator::Base
7
+ include Appydays::Loggable
8
+ include Webhookdb::Replicator::IncreaseV1Mixin
9
+
10
+ # @return [Webhookdb::Replicator::Descriptor]
11
+ def self.descriptor
12
+ return Webhookdb::Replicator::Descriptor.new(
13
+ name: "increase_wire_transfer_v1",
14
+ ctor: ->(sint) { Webhookdb::Replicator::IncreaseWireTransferV1.new(sint) },
15
+ feature_roles: [],
16
+ resource_name_singular: "Increase Wire Transfer",
17
+ supports_webhooks: true,
18
+ supports_backfill: true,
19
+ api_docs_url: "https://increase.com/documentation/api",
20
+ )
21
+ end
22
+
23
+ def _remote_key_column
24
+ return Webhookdb::Replicator::Column.new(:increase_id, TEXT, data_key: "id")
25
+ end
26
+
27
+ def _denormalized_columns
28
+ return [
29
+ Webhookdb::Replicator::Column.new(:account_number, TEXT, index: true),
30
+ Webhookdb::Replicator::Column.new(:account_id, TEXT, index: true),
31
+ Webhookdb::Replicator::Column.new(:amount, INTEGER, index: true),
32
+ Webhookdb::Replicator::Column.new(:approved_at, TIMESTAMP, data_key: ["approval", "approved_at"]),
33
+ Webhookdb::Replicator::Column.new(:created_at, TIMESTAMP, optional: true, index: true),
34
+ Webhookdb::Replicator::Column.new(:routing_number, TEXT, index: true),
35
+ Webhookdb::Replicator::Column.new(:status, TEXT),
36
+ Webhookdb::Replicator::Column.new(:template_id, TEXT),
37
+ Webhookdb::Replicator::Column.new(:transaction_id, TEXT, index: true),
38
+ Webhookdb::Replicator::Column.new(
39
+ :updated_at,
40
+ TIMESTAMP,
41
+ data_key: "created_at",
42
+ event_key: "created_at",
43
+ defaulter: :now,
44
+ optional: true,
45
+ index: true,
46
+ ),
47
+ ]
48
+ end
49
+
50
+ def _resource_and_event(request)
51
+ return self._find_resource_and_event(request.body, "wire_transfer")
52
+ end
53
+
54
+ def _update_where_expr
55
+ return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
56
+ end
57
+
58
+ def _mixin_backfill_url
59
+ return "#{self.service_integration.api_url}/wire_transfers"
60
+ end
61
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/intercom"
4
+ require "webhookdb/replicator/intercom_v1_mixin"
5
+
6
+ class Webhookdb::Replicator::IntercomContactV1 < Webhookdb::Replicator::Base
7
+ include Appydays::Loggable
8
+ include Webhookdb::Replicator::IntercomV1Mixin
9
+
10
+ def self.descriptor
11
+ return Webhookdb::Replicator::Descriptor.new(
12
+ name: "intercom_contact_v1",
13
+ ctor: self,
14
+ feature_roles: ["intercom"],
15
+ resource_name_singular: "Intercom Contact",
16
+ dependency_descriptor: Webhookdb::Replicator::IntercomMarketplaceRootV1.descriptor,
17
+ supports_backfill: true,
18
+ api_docs_url: "https://developers.intercom.com/docs/references/rest-api/api.intercom.io/Contacts/",
19
+ )
20
+ end
21
+
22
+ def _remote_key_column
23
+ return Webhookdb::Replicator::Column.new(:intercom_id, TEXT, data_key: "id")
24
+ end
25
+
26
+ def _denormalized_columns
27
+ return [
28
+ Webhookdb::Replicator::Column.new(:external_id, TEXT),
29
+ Webhookdb::Replicator::Column.new(:email, TEXT),
30
+ Webhookdb::Replicator::Column.new(:created_at, TIMESTAMP, converter: :tsat),
31
+ Webhookdb::Replicator::Column.new(:updated_at, TIMESTAMP, converter: :tsat),
32
+ ]
33
+ end
34
+
35
+ def _mixin_backfill_url = "https://api.intercom.io/contacts"
36
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/intercom"
4
+ require "webhookdb/replicator/intercom_v1_mixin"
5
+
6
+ class Webhookdb::Replicator::IntercomConversationV1 < Webhookdb::Replicator::Base
7
+ include Appydays::Loggable
8
+ include Webhookdb::Replicator::IntercomV1Mixin
9
+
10
+ def self.descriptor
11
+ return Webhookdb::Replicator::Descriptor.new(
12
+ name: "intercom_conversation_v1",
13
+ ctor: self,
14
+ feature_roles: ["intercom"],
15
+ resource_name_singular: "Intercom Conversation",
16
+ dependency_descriptor: Webhookdb::Replicator::IntercomMarketplaceRootV1.descriptor,
17
+ supports_backfill: true,
18
+ api_docs_url: "https://developers.intercom.com/docs/references/rest-api/api.intercom.io/Conversations/",
19
+ )
20
+ end
21
+
22
+ def _remote_key_column
23
+ return Webhookdb::Replicator::Column.new(:intercom_id, TEXT, data_key: "id")
24
+ end
25
+
26
+ def _denormalized_columns
27
+ return [
28
+ Webhookdb::Replicator::Column.new(:title, TEXT),
29
+ Webhookdb::Replicator::Column.new(:state, TEXT),
30
+ Webhookdb::Replicator::Column.new(:open, BOOLEAN),
31
+ Webhookdb::Replicator::Column.new(:read, BOOLEAN),
32
+ Webhookdb::Replicator::Column.new(:created_at, TIMESTAMP, converter: :tsat),
33
+ Webhookdb::Replicator::Column.new(:updated_at, TIMESTAMP, converter: :tsat),
34
+ ]
35
+ end
36
+
37
+ def _mixin_backfill_url = "https://api.intercom.io/conversations"
38
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Webhookdb::Replicator::IntercomMarketplaceRootV1 < 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: "intercom_marketplace_root_v1",
10
+ ctor: self,
11
+ feature_roles: ["intercom"],
12
+ resource_name_singular: "Intercom Auth",
13
+ resource_name_plural: "Intercom Auth",
14
+ supports_backfill: true,
15
+ description: "You can replicate your Intercom data to WebhookDB Cloud in one click using " \
16
+ "the [Intercom App Store](https://www.intercom.com/app-store).",
17
+ )
18
+ end
19
+
20
+ def _remote_key_column
21
+ return Webhookdb::Replicator::Column.new(:ignore_id, INTEGER)
22
+ end
23
+
24
+ def _denormalized_columns
25
+ return []
26
+ end
27
+
28
+ def _upsert_webhook(**_kwargs)
29
+ raise NotImplementedError("This is a stub integration only for auth purposes.")
30
+ end
31
+
32
+ def _fetch_backfill_page(*)
33
+ return [], nil
34
+ end
35
+
36
+ def webhook_response(_request)
37
+ raise NotImplementedError("This is a stub integration only for auth purposes.")
38
+ end
39
+
40
+ def calculate_backfill_state_machine
41
+ step = Webhookdb::Replicator::StateMachineStep.new
42
+ step.output = "This integration cannot be modified through the command line."
43
+ step.completed
44
+ return step
45
+ end
46
+
47
+ def get_auth_headers
48
+ return {
49
+ "Authorization" => "Bearer #{self.service_integration.backfill_key}",
50
+ "Accept" => "application/json",
51
+ }
52
+ end
53
+
54
+ def build_dependents
55
+ org = self.service_integration.organization
56
+ contact_sint = Webhookdb::ServiceIntegration.create_disambiguated(
57
+ "intercom_contact_v1",
58
+ organization: org,
59
+ depends_on: self.service_integration,
60
+ )
61
+ conversation_sint = Webhookdb::ServiceIntegration.create_disambiguated(
62
+ "intercom_conversation_v1",
63
+ organization: org,
64
+ depends_on: self.service_integration,
65
+ )
66
+ contact_sint.replicator._enqueue_backfill_jobs(incremental: true)
67
+ conversation_sint.replicator._enqueue_backfill_jobs(incremental: true)
68
+ end
69
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Webhookdb::Replicator::IntercomV1Mixin
4
+ # @return [Webhookdb::ServiceIntegration]
5
+
6
+ # Quick note on these Intercom integrations: although we will technically be bringing in information from webhooks,
7
+ # all webhooks for the WebhookDB app will use a single endpoint and we use the WebhookDB app's Client Secret for
8
+ # webhook verification, which means that webhooks actually don't require any setup on the integration level. Thus,
9
+ # `supports_webhooks` is false.
10
+ def find_auth_integration
11
+ # rubocop:disable Naming/MemoizedInstanceVariableName
12
+ return @auth ||= Webhookdb::Replicator.find_at_root!(self.service_integration,
13
+ service_name: "intercom_marketplace_root_v1",)
14
+ # rubocop:enable Naming/MemoizedInstanceVariableName
15
+ end
16
+
17
+ def intercom_auth_headers
18
+ root_sint = self.find_auth_integration
19
+ return Webhookdb::Intercom.auth_headers(root_sint.backfill_key)
20
+ end
21
+
22
+ def auth_credentials?
23
+ auth = self.find_auth_integration
24
+ return auth.backfill_key.present?
25
+ end
26
+
27
+ def _resource_and_event(request)
28
+ body = request.body
29
+ return body.fetch("data").fetch("item"), body if body.fetch("type") == "notification_event"
30
+ return body, nil
31
+ end
32
+
33
+ def _update_where_expr
34
+ return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
35
+ end
36
+
37
+ def _timestamp_column_name
38
+ return :updated_at
39
+ end
40
+
41
+ def _webhook_response(request)
42
+ # info for debugging
43
+ intercom_auth = request.env["HTTP_X_HUB_SIGNATURE"]
44
+
45
+ return Webhookdb::WebhookResponse.error("missing hmac") if intercom_auth.nil?
46
+ request.body.rewind
47
+ request_data = request.body.read
48
+ verified = Webhookdb::Intercom.verify_webhook(request_data, intercom_auth)
49
+ return Webhookdb::WebhookResponse.ok if verified
50
+ return Webhookdb::WebhookResponse.error("invalid hmac")
51
+ end
52
+
53
+ # @return [Webhookdb::Replicator::StateMachineStep]
54
+ def calculate_backfill_state_machine
55
+ # can inherit credentials from the auth dependency
56
+ if (step = self.calculate_dependency_state_machine_step(dependency_help: ""))
57
+ return step
58
+ end
59
+ step = Webhookdb::Replicator::StateMachineStep.new
60
+ step.output = %(We will start replicating #{self.resource_name_singular} information into your WebhookDB database.
61
+
62
+ #{self._query_help_output(prefix: "Once data is available, you can query #{self.resource_name_plural}.")})
63
+ return step.completed
64
+ end
65
+
66
+ def on_dependency_webhook_upsert(_replicator, _payload, *)
67
+ return
68
+ end
69
+
70
+ def _mixin_backfill_url
71
+ raise NotImplementedError
72
+ end
73
+
74
+ def _fetch_backfill_page(pagination_token, **_kwargs)
75
+ unless self.auth_credentials?
76
+ raise Webhookdb::Replicator::CredentialsMissing,
77
+ "This integration requires that the Intercom Auth integration has a valid Auth Token"
78
+ end
79
+
80
+ query = {per_page: Webhookdb::Intercom.page_size}
81
+ # Intercom started 500ing with this set to empty.
82
+ query[:starting_after] = pagination_token if pagination_token
83
+ begin
84
+ response = Webhookdb::Http.get(
85
+ self._mixin_backfill_url,
86
+ query:,
87
+ headers: self.intercom_auth_headers,
88
+ logger: self.logger,
89
+ timeout: Webhookdb::Intercom.http_timeout,
90
+ )
91
+ rescue Webhookdb::Http::Error => e
92
+ # We are looking to catch the "api plan restricted" error. This is always a 403 and every
93
+ # 403 will be an "api plan restricted" error according to the API documentation. Because we
94
+ # specify the API version in our headers we can expect that this won't change.
95
+ raise e unless e.status == 403
96
+ self.logger.warn("intercom_api_restricted", intercom_error: e.body)
97
+ # We should basically noop here, i.e. pretend that the page is empty, so that we don't trigger
98
+ # a TypeError in the backfiller.
99
+ return [], nil
100
+ end
101
+ data = response.parsed_response.fetch("data", [])
102
+ starting_after = response.parsed_response.dig("pages", "next", "starting_after")
103
+ return data, starting_after
104
+ end
105
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/redis"
4
+
5
+ module Webhookdb::Replicator::OAuthRefreshAccessTokenMixin
6
+ EXPIRATION_BUFFER = 60
7
+
8
+ def oauth_cache_key_namespace = raise NotImplementedError("return something like 'gcalv1'")
9
+ def oauth_token_url = raise NotImplementedError("return something like https://someservice/oauth2/token")
10
+
11
+ def oauth_http_timeout = raise NotImplementedError("return a timeout value for oauth http requests")
12
+
13
+ def oauth_access_token_cache_key(oauth_user_id)
14
+ parts = [self.oauth_cache_key_namespace, "atok", self.service_integration.id.to_s, oauth_user_id]
15
+ return Webhookdb::Redis.cache_key(parts)
16
+ end
17
+
18
+ def delete_oauth_access_token(oauth_user_id)
19
+ key = self.oauth_access_token_cache_key(oauth_user_id)
20
+ Webhookdb::Redis.cache.with do |r|
21
+ r.call("DEL", key)
22
+ end
23
+ end
24
+
25
+ def _with_oauth_access_token(oauth_user_id, get_refresh_token)
26
+ key = self.oauth_access_token_cache_key(oauth_user_id)
27
+ Webhookdb::Redis.cache.with do |r|
28
+ got = r.call("GET", key)
29
+ if got
30
+ yield got
31
+ else
32
+ self.logger.info "creating_access_token"
33
+ form_body = URI.encode_www_form(
34
+ {
35
+ client_id: self.service_integration.backfill_key,
36
+ client_secret: self.service_integration.backfill_secret,
37
+ refresh_token: get_refresh_token.call,
38
+ grant_type: "refresh_token",
39
+ },
40
+ )
41
+ resp = Webhookdb::Http.post(
42
+ self.oauth_token_url,
43
+ form_body,
44
+ headers: {
45
+ "Content-Type" => "application/x-www-form-urlencoded",
46
+ "charset" => "utf-8",
47
+ },
48
+ logger: self.logger,
49
+ timeout: self.oauth_http_timeout,
50
+ )
51
+ access_token = resp.parsed_response.fetch("access_token")
52
+ r.call("SETEX", key, resp.parsed_response["expires_in"] - EXPIRATION_BUFFER, access_token)
53
+ yield access_token
54
+ end
55
+ end
56
+ end
57
+
58
+ def force_set_oauth_access_token(oauth_user_id, access_token, expires_in: 60.minutes.to_i)
59
+ key = self.oauth_access_token_cache_key(oauth_user_id)
60
+ Webhookdb::Redis.cache.with do |r|
61
+ r.call("SETEX", key, expires_in, access_token)
62
+ return access_token
63
+ end
64
+ end
65
+ end