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,334 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/icalendar"
4
+ require "webhookdb/windows_tz"
5
+
6
+ class Webhookdb::Replicator::IcalendarEventV1 < Webhookdb::Replicator::Base
7
+ include Appydays::Loggable
8
+
9
+ def documentation_url = Webhookdb::Icalendar::DOCUMENTATION_URL
10
+
11
+ # @return [Webhookdb::Replicator::Descriptor]
12
+ def self.descriptor
13
+ return Webhookdb::Replicator::Descriptor.new(
14
+ name: "icalendar_event_v1",
15
+ ctor: ->(sint) { Webhookdb::Replicator::IcalendarEventV1.new(sint) },
16
+ dependency_descriptor: Webhookdb::Replicator::IcalendarCalendarV1.descriptor,
17
+ feature_roles: [],
18
+ resource_name_singular: "iCalendar Event",
19
+ supports_webhooks: true,
20
+ description: "Individual events in an icalendar. See icalendar_calendar_v1.",
21
+ api_docs_url: "https://icalendar.org/",
22
+ )
23
+ end
24
+
25
+ CONV_REMOTE_KEY = Webhookdb::Replicator::Column::IsomorphicProc.new(
26
+ ruby: lambda do |_, resource:, **_|
27
+ "#{resource.fetch('calendar_external_id')}-#{resource.fetch('UID').fetch('v')}"
28
+ end,
29
+ # Because this is a non-nullable key, we never need this in SQL
30
+ sql: ->(_) { Sequel.lit("'do not use'") },
31
+ )
32
+
33
+ # Return tuple of parsed date or datetime, and a boolean of whether
34
+ # the timezone could be parsed (true if date was parsed).
35
+ # @return [Array<Time,Date,true,false,nil>]
36
+ def self.entry_to_date_or_datetime(entry)
37
+ return [self.entry_to_date(entry), true] if self.value_is_date_str?(entry.fetch("v"))
38
+ return self.entry_to_datetime(entry)
39
+ end
40
+
41
+ def self.entry_is_date_str?(e) = self.value_is_date_str?(e.fetch("v"))
42
+ def self.value_is_date_str?(v) = v.length === 8
43
+
44
+ # Return tuple of parsed datetime, and a boolean of whether
45
+ # the timezone could be parsed.
46
+ # @return [Array<Time,true,false,nil>]
47
+ def self.entry_to_datetime(entry)
48
+ value = entry.fetch("v")
49
+ raise ArgumentError, "do not pass a date string" if self.value_is_date_str?(value)
50
+ return [Time.strptime(value, "%Y%m%dT%H%M%S%Z"), true] if value.end_with?("Z")
51
+ if (tzid = entry["TZID"])
52
+ return self._parse_time_with_tzid(value, tzid)
53
+ end
54
+ return [Time.find_zone!("UTC").parse(value), false]
55
+ end
56
+
57
+ # @return [Date,nil]
58
+ def self.entry_to_date(entry)
59
+ value = entry.fetch("v")
60
+ raise ArgumentError, "must pass a date string" unless self.value_is_date_str?(value)
61
+ return Date.strptime(value, "%Y%m%d")
62
+ end
63
+
64
+ CONV_DATE = Webhookdb::Replicator::Column::IsomorphicProc.new(
65
+ ruby: lambda do |entry, **|
66
+ self.entry_to_date(entry) if entry.is_a?(Hash) && self.entry_is_date_str?(entry)
67
+ end,
68
+ sql: ->(_) { raise NotImplementedError },
69
+ )
70
+ CONV_DATETIME = Webhookdb::Replicator::Column::IsomorphicProc.new(
71
+ ruby: lambda do |entry, **|
72
+ if entry.is_a?(Hash)
73
+ if self.entry_is_date_str?(entry)
74
+ nil
75
+ else
76
+ self.entry_to_datetime(entry).first
77
+ end
78
+ else
79
+ # Entry may be a time if this was from the defaulter
80
+ entry
81
+ end
82
+ end,
83
+ sql: ->(_) { raise NotImplementedError },
84
+ )
85
+ CONV_MISSING_TZ = Webhookdb::Replicator::Column::IsomorphicProc.new(
86
+ ruby: lambda do |entry, **|
87
+ may_have_missing_tz = entry.is_a?(Hash) && !self.entry_is_date_str?(entry)
88
+ if may_have_missing_tz
89
+ tzparsed = self.entry_to_datetime(entry)[1]
90
+ !tzparsed
91
+ else
92
+ false
93
+ end
94
+ end,
95
+ sql: ->(_) { Sequel[false] },
96
+ )
97
+ CONV_GEO_LAT = Webhookdb::Replicator::Column.converter_array_element(index: 0, sep: ";", cls: DECIMAL)
98
+ CONV_GEO_LNG = Webhookdb::Replicator::Column.converter_array_element(index: 1, sep: ";", cls: DECIMAL)
99
+ CONV_COMMA_SEP_ARRAY = Webhookdb::Replicator::Column::IsomorphicProc.new(
100
+ ruby: lambda do |entry, **|
101
+ next [] if entry.nil?
102
+ entries = []
103
+ entry.each do |e|
104
+ entries.concat(e.fetch("v").split(",").map(&:strip))
105
+ end
106
+ entries
107
+ end,
108
+ sql: ->(_) { raise NotImplementedError },
109
+ )
110
+
111
+ def _remote_key_column
112
+ return Webhookdb::Replicator::Column.new(
113
+ :compound_identity,
114
+ TEXT,
115
+ data_key: "<compound key, see converter>",
116
+ index: true,
117
+ converter: CONV_REMOTE_KEY,
118
+ optional: true, # This is done via the converter, data_key never exists
119
+ )
120
+ end
121
+
122
+ def _denormalized_columns
123
+ col = Webhookdb::Replicator::Column
124
+ tsconv = {converter: CONV_DATETIME}
125
+ dateconv = {converter: CONV_DATE}
126
+ return [
127
+ col.new(:calendar_external_id, TEXT, index: true),
128
+ col.new(:uid, TEXT, data_key: ["UID", "v"], index: true),
129
+ col.new(:row_updated_at, TIMESTAMP, index: true, defaulter: :now, optional: true),
130
+ col.new(:last_modified_at,
131
+ TIMESTAMP,
132
+ index: true,
133
+ data_key: "LAST-MODIFIED",
134
+ defaulter: :now,
135
+ optional: true,
136
+ **tsconv,),
137
+ col.new(:created_at, TIMESTAMP, optional: true, data_key: "CREATED", **tsconv),
138
+ col.new(:start_at, TIMESTAMP, index: true, index_not_null: true, data_key: "DTSTART", **tsconv),
139
+ # This is True when start/end at fields are missing timezones in the underlying feed.
140
+ # Their timestamps are in UTC.
141
+ col.new(:missing_timezone, BOOLEAN, data_key: "DTSTART", converter: CONV_MISSING_TZ),
142
+ col.new(:end_at, TIMESTAMP, index: true, index_not_null: true, data_key: "DTEND", optional: true, **tsconv),
143
+ col.new(:start_date, DATE, index: true, index_not_null: true, data_key: "DTSTART", **dateconv),
144
+ col.new(:end_date, DATE, index: true, index_not_null: true, data_key: "DTEND", optional: true, **dateconv),
145
+ col.new(:status, TEXT, data_key: ["STATUS", "v"], optional: true),
146
+ col.new(:categories, TEXT_ARRAY, data_key: ["CATEGORIES"], optional: true, converter: CONV_COMMA_SEP_ARRAY),
147
+ col.new(:priority, INTEGER, data_key: ["PRIORITY", "v"], optional: true, converter: col::CONV_TO_I),
148
+ col.new(:geo_lat, DECIMAL, data_key: ["GEO", "v"], optional: true, converter: CONV_GEO_LAT),
149
+ col.new(:geo_lng, DECIMAL, data_key: ["GEO", "v"], optional: true, converter: CONV_GEO_LNG),
150
+ col.new(:classification, TEXT, data_key: ["CLASS", "v"], optional: true),
151
+ col.new(:recurring_event_id, TEXT, optional: true, index: true, index_not_null: true),
152
+ col.new(:recurring_event_sequence, INTEGER, optional: true),
153
+ ]
154
+ end
155
+
156
+ def _resource_and_event(request)
157
+ return request.body, nil
158
+ end
159
+
160
+ def _resource_to_data(r, *)
161
+ data = r.dup
162
+ data.delete("calendar_external_id")
163
+ data.delete("recurring_event_id")
164
+ data.delete("recurring_event_sequence")
165
+ return data
166
+ end
167
+
168
+ # @return [Array<Webhookdb::Replicator::IndexSpec>]
169
+ def _extra_index_specs
170
+ return [
171
+ Webhookdb::Replicator::IndexSpec.new(
172
+ columns: [:calendar_external_id, :start_at, :end_at],
173
+ where: Sequel[:status].is_distinct_from("CANCELLED") & (Sequel[:start_at] !~ nil),
174
+ ),
175
+ Webhookdb::Replicator::IndexSpec.new(
176
+ columns: [:calendar_external_id, :start_date, :end_date],
177
+ where: Sequel[:status].is_distinct_from("CANCELLED") & (Sequel[:start_date] !~ nil),
178
+ ),
179
+ ]
180
+ end
181
+
182
+ def _update_where_expr
183
+ return self.qualified_table_sequel_identifier[:last_modified_at] < Sequel[:excluded][:last_modified_at]
184
+ end
185
+
186
+ # @param [Array<String>] lines
187
+ # @return [String]
188
+ def self._compact_vevent_lines(lines)
189
+ # Walk backwards from the end, joining continuation lines.
190
+ # Very hard to reason about this if it's written using normal array stuff.
191
+ (1..(lines.length - 1)).reverse_each do |idx|
192
+ line = lines[idx]
193
+ prevline = lines[idx - 1]
194
+ next unless line.start_with?(/\s+/)
195
+ line.lstrip!
196
+ prevline << line
197
+ lines.delete_at(idx)
198
+ end
199
+ s = lines.join("\n")
200
+ return s
201
+ end
202
+
203
+ def self.vevent_to_hash(vevent_lines)
204
+ result = {}
205
+ vevent_str = self._compact_vevent_lines(vevent_lines)
206
+ nest_depth = 0
207
+ vevent_str.lines.each do |line|
208
+ if line.start_with?("BEGIN")
209
+ nest_depth += 1
210
+ next
211
+ elsif line.start_with?("END")
212
+ nest_depth -= 1
213
+ next
214
+ end
215
+ next if nest_depth > 1
216
+ line.strip!
217
+ next if line.empty?
218
+ keyname, value, params = self._parse_line(line)
219
+ unless value.nil?
220
+ value.gsub!("\\r\\n", "\r\n")
221
+ value.gsub!("\\n", "\n")
222
+ value.gsub!("\\t", "\t")
223
+ end
224
+ entry = {"v" => value}
225
+ entry.merge!(params)
226
+ if ARRAY_KEYS.include?(keyname)
227
+ result[keyname] ||= []
228
+ result[keyname] << entry
229
+ else
230
+ result[keyname] = entry
231
+ end
232
+ end
233
+ return result
234
+ end
235
+
236
+ # https://datatracker.ietf.org/doc/html/rfc5545#section-3.6.1
237
+ # The following are OPTIONAL, and MAY occur more than once.
238
+ ARRAY_KEYS = [
239
+ "ATTACH",
240
+ "ATTENDEE",
241
+ "CATEGORIES",
242
+ "COMMENT",
243
+ "CONTACT",
244
+ "EXDATE",
245
+ "RSTATUS",
246
+ "RELATED",
247
+ "RESOURCES",
248
+ "RDATE",
249
+ "X-PROP",
250
+ "IANA-PROP",
251
+ ].freeze
252
+
253
+ NAME = "[-a-zA-Z0-9]+"
254
+ QSTR = '"[^"]*"'
255
+ PTEXT = '[^";:,]*'
256
+ PVALUE = "(?:#{QSTR}|#{PTEXT})".freeze
257
+ PARAM = "(#{NAME})=(#{PVALUE}(?:,#{PVALUE})*)".freeze
258
+ VALUE = ".*"
259
+ LINE = "(?<name>#{NAME})(?<params>(?:;#{PARAM})*):(?<value>#{VALUE})".freeze
260
+
261
+ # @param input [String]
262
+ def self._parse_line(input)
263
+ parts = /#{LINE}/o.match(input)
264
+ return input, nil, {} if parts.nil?
265
+ params = {}
266
+ parts[:params].scan(/#{PARAM}/o) do |match|
267
+ param_name = match[0]
268
+ # params[param_name] ||= []
269
+ match[1].scan(/#{PVALUE}/o) do |param_value|
270
+ if param_value.size.positive?
271
+ param_value = param_value.gsub(/\A"|"\z/, "")
272
+ params[param_name] = param_value
273
+ # params["x-tz-info"] = timezone_store.retrieve param_value if param_name == "tzid"
274
+ end
275
+ end
276
+ end
277
+ return parts[:name], parts[:value], params
278
+ end
279
+
280
+ # Given a tzid and value for a timestamp, return a Time (with a timezone).
281
+ # While there's no formal naming scheme, we see the following forms:
282
+ # - valid names like America/Los_Angeles, US/Eastern
283
+ # - dashes, like America-Los_Angeles, US-Eastern
284
+ # - Offsets, like GMT-0700'
285
+ #
286
+ # In theory this can be any value, and must be given in the calendar feed (VTIMEZONE).
287
+ # However that is extremely difficult; even the icalendar gem doesn't seem to do it 100% right.
288
+ # We can solve for this if needed; in the meantime, log it in Sentry and use UTC.
289
+ #
290
+ # If the zone cannot be parsed, assume UTC.
291
+ #
292
+ # Return a tuple of [Time, true if the zone could be parsed].
293
+ # If the zone cannot be parsed, you usually want to log or store it.
294
+ def self._parse_time_with_tzid(value, tzid)
295
+ if (zone = Time.find_zone(tzid.tr("-", "/")))
296
+ return [zone.parse(value), true]
297
+ end
298
+ if /^(GMT|UTC)[+-]\d\d\d\d$/.match?(tzid)
299
+ offset = tzid[3..]
300
+ return [Time.parse(value + offset), true]
301
+ end
302
+ if (zone = Webhookdb::WindowsTZ.windows_name_to_tz[tzid])
303
+ return [zone.parse(value), true]
304
+ end
305
+ zone = Time.find_zone!("UTC")
306
+ return [zone.parse(value), false]
307
+ end
308
+
309
+ def on_dependency_webhook_upsert(_ical_svc, _ical_row, **)
310
+ # We use an async job to sync when the dependency syncs
311
+ return
312
+ end
313
+
314
+ def calculate_webhook_state_machine
315
+ if (step = self.calculate_dependency_state_machine_step(dependency_help: ""))
316
+ return step
317
+ end
318
+ step = Webhookdb::Replicator::StateMachineStep.new
319
+ step.output = %(Great! You are all set.
320
+ Refer to https://docs.webhookdb.com/guides/icalendar/ for detailed instructions
321
+ on syncing data from iCalendar/ics feeds.
322
+
323
+ #{self._query_help_output(prefix: "Once data is available, you can query #{self.resource_name_plural}")})
324
+ return step.completed
325
+ end
326
+
327
+ def backfill_not_supported_message
328
+ return %(#{self.resource_name_singular} does not support backfilling.
329
+ See https://docs.webhookdb.com/guides/icalendar/ for instructions on setting up your integration.
330
+
331
+ You can POST 'SYNC' messages to WebhookDB to force-sync a user's feed,
332
+ though keep in mind calendar providers only refresh feeds periodically.)
333
+ end
334
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/increase"
4
+ require "webhookdb/replicator/increase_v1_mixin"
5
+
6
+ class Webhookdb::Replicator::IncreaseAccountNumberV1 < 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_account_number_v1",
14
+ ctor: ->(sint) { Webhookdb::Replicator::IncreaseAccountNumberV1.new(sint) },
15
+ feature_roles: [],
16
+ resource_name_singular: "Increase Account Number",
17
+ supports_webhooks: true,
18
+ supports_backfill: true,
19
+ api_docs_url: "https://icalendar.org/",
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(:account_number, TEXT, index: true),
31
+ Webhookdb::Replicator::Column.new(:name, TEXT),
32
+ Webhookdb::Replicator::Column.new(:routing_number, TEXT, index: true),
33
+ Webhookdb::Replicator::Column.new(
34
+ :row_created_at,
35
+ TIMESTAMP,
36
+ data_key: "created_at",
37
+ event_key: "created_at",
38
+ optional: true,
39
+ defaulter: :now,
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
+ ]
53
+ end
54
+
55
+ def _timestamp_column_name
56
+ return :row_updated_at
57
+ end
58
+
59
+ def _update_where_expr
60
+ return self.qualified_table_sequel_identifier[:row_updated_at] < Sequel[:excluded][:row_updated_at]
61
+ end
62
+
63
+ def _resource_and_event(request)
64
+ return self._find_resource_and_event(request.body, "account_number")
65
+ end
66
+
67
+ def _upsert_update_expr(inserting, **_kwargs)
68
+ update = super
69
+ # Only set created_at if it's not set so the initial insert isn't modified.
70
+ self._coalesce_excluded_on_update(update, [:row_created_at])
71
+ return update
72
+ end
73
+
74
+ def _mixin_backfill_url
75
+ return "#{self.service_integration.api_url}/account_numbers"
76
+ end
77
+ 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::IncreaseAccountTransferV1 < 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_account_transfer_v1",
14
+ ctor: ->(sint) { Webhookdb::Replicator::IncreaseAccountTransferV1.new(sint) },
15
+ feature_roles: [],
16
+ resource_name_singular: "Increase Account 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(:amount, INTEGER, index: true),
30
+ Webhookdb::Replicator::Column.new(:account_id, TEXT, index: true),
31
+ Webhookdb::Replicator::Column.new(:canceled_at, TIMESTAMP, data_key: ["cancellation", "canceled_at"],
32
+ optional: true,),
33
+ Webhookdb::Replicator::Column.new(:created_at, TIMESTAMP, index: true),
34
+ Webhookdb::Replicator::Column.new(:destination_account_id, TEXT, index: true),
35
+ Webhookdb::Replicator::Column.new(:destination_transaction_id, TEXT, index: true),
36
+ Webhookdb::Replicator::Column.new(:status, TEXT),
37
+ Webhookdb::Replicator::Column.new(:template_id, TEXT),
38
+ Webhookdb::Replicator::Column.new(:transaction_id, TEXT, index: true),
39
+ Webhookdb::Replicator::Column.new(
40
+ :updated_at,
41
+ TIMESTAMP,
42
+ data_key: "created_at",
43
+ event_key: "created_at",
44
+ defaulter: :now,
45
+ index: true,
46
+ ),
47
+ ]
48
+ end
49
+
50
+ def _update_where_expr
51
+ return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
52
+ end
53
+
54
+ def _resource_and_event(request)
55
+ return self._find_resource_and_event(request.body, "account_transfer")
56
+ end
57
+
58
+ def _mixin_backfill_url
59
+ return "#{self.service_integration.api_url}/account_transfers"
60
+ end
61
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/increase"
4
+ require "webhookdb/replicator/increase_v1_mixin"
5
+
6
+ class Webhookdb::Replicator::IncreaseAccountV1 < 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_account_v1",
14
+ ctor: ->(sint) { Webhookdb::Replicator::IncreaseAccountV1.new(sint) },
15
+ feature_roles: [],
16
+ resource_name_singular: "Increase Account",
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(:balance, INTEGER, index: true),
30
+ Webhookdb::Replicator::Column.new(
31
+ :created_at,
32
+ TIMESTAMP,
33
+ data_key: "created_at",
34
+ defaulter: :now,
35
+ index: true,
36
+ ),
37
+ Webhookdb::Replicator::Column.new(:entity_id, TEXT, index: true),
38
+ Webhookdb::Replicator::Column.new(:interest_accrued, DECIMAL),
39
+ Webhookdb::Replicator::Column.new(:name, TEXT),
40
+ Webhookdb::Replicator::Column.new(:status, TEXT),
41
+ Webhookdb::Replicator::Column.new(
42
+ :updated_at,
43
+ TIMESTAMP,
44
+ data_key: "created_at",
45
+ event_key: "created_at",
46
+ defaulter: :now,
47
+ index: true,
48
+ ),
49
+ ]
50
+ end
51
+
52
+ def _update_where_expr
53
+ return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
54
+ end
55
+
56
+ def _resource_and_event(request)
57
+ return self._find_resource_and_event(request.body, "account")
58
+ end
59
+
60
+ def _mixin_backfill_url
61
+ return "#{self.service_integration.api_url}/accounts"
62
+ end
63
+ end
@@ -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::IncreaseACHTransferV1 < 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_ach_transfer_v1",
14
+ ctor: ->(sint) { Webhookdb::Replicator::IncreaseACHTransferV1.new(sint) },
15
+ feature_roles: [],
16
+ resource_name_singular: "Increase ACH 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(
33
+ :created_at,
34
+ TIMESTAMP,
35
+ data_key: "created_at",
36
+ index: true,
37
+ ),
38
+ Webhookdb::Replicator::Column.new(:routing_number, TEXT, index: true),
39
+ Webhookdb::Replicator::Column.new(:status, TEXT),
40
+ Webhookdb::Replicator::Column.new(:transaction_id, TEXT, index: true),
41
+ Webhookdb::Replicator::Column.new(
42
+ :updated_at,
43
+ TIMESTAMP,
44
+ data_key: "created_at",
45
+ event_key: "created_at",
46
+ defaulter: :now,
47
+ index: true,
48
+ ),
49
+ ]
50
+ end
51
+
52
+ def _prepare_for_insert(resource, event, request, enrichment)
53
+ # created_at is marked required, but to skip on nil.
54
+ # This will preserve its existing value when we update the webhook.
55
+ resource["created_at"] = nil if event&.fetch("event") == "updated"
56
+ return super
57
+ end
58
+
59
+ def _upsert_update_expr(inserting, enrichment: nil)
60
+ update = super
61
+ update[:data] = Sequel.lit("#{self.service_integration.table_name}.data || excluded.data")
62
+ return update
63
+ end
64
+
65
+ def _update_where_expr
66
+ return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
67
+ end
68
+
69
+ def _resource_and_event(request)
70
+ resource, event = self._find_resource_and_event(request.body, "ach_transfer")
71
+ return nil, nil if (resource && resource["type"]) == "inbound_ach_transfer"
72
+ return resource, event
73
+ end
74
+
75
+ def _mixin_backfill_url
76
+ return "#{self.service_integration.api_url}/transfers/achs"
77
+ end
78
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webhookdb/increase"
4
+ require "webhookdb/replicator/increase_v1_mixin"
5
+
6
+ class Webhookdb::Replicator::IncreaseCheckTransferV1 < 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_check_transfer_v1",
14
+ ctor: ->(sint) { Webhookdb::Replicator::IncreaseCheckTransferV1.new(sint) },
15
+ feature_roles: [],
16
+ resource_name_singular: "Increase Check 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_id, TEXT, index: true),
30
+ Webhookdb::Replicator::Column.new(:address_line1, TEXT),
31
+ Webhookdb::Replicator::Column.new(:address_city, TEXT),
32
+ Webhookdb::Replicator::Column.new(:address_state, TEXT),
33
+ Webhookdb::Replicator::Column.new(:address_zip, TEXT, index: true),
34
+ Webhookdb::Replicator::Column.new(:amount, INTEGER, index: true),
35
+ Webhookdb::Replicator::Column.new(:created_at, TIMESTAMP, data_key: "created_at", index: true),
36
+ Webhookdb::Replicator::Column.new(:mailed_at, TIMESTAMP),
37
+ Webhookdb::Replicator::Column.new(:recipient_name, TEXT),
38
+ Webhookdb::Replicator::Column.new(:status, TEXT),
39
+ Webhookdb::Replicator::Column.new(:submitted_at, TIMESTAMP, index: true),
40
+ Webhookdb::Replicator::Column.new(:template_id, TEXT),
41
+ Webhookdb::Replicator::Column.new(:transaction_id, TEXT, index: true),
42
+ Webhookdb::Replicator::Column.new(
43
+ :updated_at,
44
+ TIMESTAMP,
45
+ data_key: "created_at",
46
+ event_key: "created_at",
47
+ defaulter: :now,
48
+ index: true,
49
+ ),
50
+ ]
51
+ end
52
+
53
+ def _update_where_expr
54
+ return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
55
+ end
56
+
57
+ def _resource_and_event(request)
58
+ return self._find_resource_and_event(request.body, "check_transfer")
59
+ end
60
+
61
+ def _mixin_backfill_url
62
+ return "#{self.service_integration.api_url}/check_transfers"
63
+ end
64
+ end