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,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "amigo/job"
4
+
5
+ require "webhookdb/async"
6
+
7
+ module Webhookdb::Async::Job
8
+ def self.extended(cls)
9
+ cls.extend Amigo::Job
10
+ cls.include(InstanceMethods)
11
+ end
12
+
13
+ module InstanceMethods
14
+ def with_log_tags(tags, &)
15
+ Webhookdb::Async::JobLogger.with_log_tags(tags, &)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/loggable/sidekiq_job_logger"
4
+
5
+ class Webhookdb::Async::JobLogger < Appydays::Loggable::SidekiqJobLogger
6
+ protected def slow_job_seconds
7
+ return Webhookdb::Async.slow_job_seconds
8
+ end
9
+
10
+ def self.durable_job_failure_notifier(job)
11
+ # See https://github.com/sidekiq/sidekiq/wiki/Job-Format#activejob-middleware-format
12
+ # for job format.
13
+ job = job.dup
14
+ # These fields always exist.
15
+ jargs = job.delete("args")
16
+ jcls = job.delete("class")
17
+ jid = job.delete("jid")
18
+ jq = job.delete("queue")
19
+ jcreated = job.delete("created_at")
20
+ safe_fields = [
21
+ {title: "Job ID", value: jid, short: true},
22
+ {title: "Job Class", value: "`#{jcls}`", short: true},
23
+ {title: "Args", value: "```#{jargs.to_json}```"},
24
+ {title: "Queue", value: jq, short: true},
25
+ {title: "Created At", value: self._ts("created_at", jcreated), short: true},
26
+ ]
27
+ # The remaining fields can be added dynamically.
28
+ other_fields = job.compact.
29
+ map { |k, v| {title: k.humanize, value: self._ts(k, v), short: true} }
30
+ Webhookdb::DeveloperAlert.new(
31
+ subsystem: "Job Died",
32
+ emoji: ":zombie:",
33
+ fallback: "Job #{jcls}[#{jid}][#{jargs}] moved to DeadSet",
34
+ fields: safe_fields + other_fields,
35
+ ).emit
36
+ end
37
+
38
+ def self._ts(k, v)
39
+ return nil if v.nil?
40
+ return v unless k.end_with?("_at")
41
+ return Time.at(v) if v.is_a?(Numeric)
42
+ return Time.parse(v) if v.is_a?(String)
43
+ return v
44
+ end
45
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "amigo/scheduled_job"
4
+
5
+ require "webhookdb/async"
6
+
7
+ module Webhookdb::Async::ScheduledJob
8
+ def self.extended(cls)
9
+ cls.extend Amigo::ScheduledJob
10
+ cls.include(InstanceMethods)
11
+ end
12
+
13
+ module InstanceMethods
14
+ def with_log_tags(tags, &)
15
+ Webhookdb::Async::JobLogger.with_log_tags(tags, &)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "amigo/retry"
4
+ require "amigo/durable_job"
5
+ require "amigo/rate_limited_error_handler"
6
+ require "appydays/configurable"
7
+ require "appydays/loggable"
8
+ require "sentry-sidekiq"
9
+ require "sidekiq"
10
+ require "sidekiq-cron"
11
+
12
+ Sidekiq.strict_args!
13
+
14
+ require "webhookdb"
15
+
16
+ module Webhookdb::Async
17
+ include Appydays::Configurable
18
+ include Appydays::Loggable
19
+ extend Webhookdb::MethodUtilities
20
+
21
+ require "webhookdb/async/job_logger"
22
+ require "webhookdb/async/audit_logger"
23
+
24
+ configurable(:async) do
25
+ # The number of (Float) seconds that should be considered "slow" for a job.
26
+ # Jobs that take longer than this amount of time will be logged
27
+ # at `warn` level.
28
+ setting :slow_job_seconds, 1.0
29
+
30
+ setting :sidekiq_redis_url, "redis://localhost:6379/0", key: "REDIS_URL"
31
+ setting :sidekiq_redis_provider, ""
32
+ # For sidekiq web UI. Randomize a default so they will only be useful if set.
33
+ setting :web_username, SecureRandom.hex(8)
34
+ setting :web_password, SecureRandom.hex(8)
35
+
36
+ setting :error_reporting_sample_rate, 0.1
37
+ setting :error_reporting_ttl, 120
38
+
39
+ after_configured do
40
+ # Very hard to to test this, so it's not tested.
41
+ url = self.sidekiq_redis_provider.present? ? ENV.fetch(self.sidekiq_redis_provider, nil) : self.sidekiq_redis_url
42
+ redis_params = {url:}
43
+ if url.start_with?("rediss:") && ENV["HEROKU_APP_ID"]
44
+ # rediss: schema is Redis with SSL. They use self-signed certs, so we have to turn off SSL verification.
45
+ # There is not a clear KB on this, you have to piece it together from Heroku and Sidekiq docs.
46
+ redis_params[:ssl_params] = {verify_mode: OpenSSL::SSL::VERIFY_NONE}
47
+ end
48
+ Sidekiq.configure_server do |config|
49
+ config.redis = redis_params
50
+ config.options[:job_logger] = Webhookdb::Async::JobLogger
51
+ # We do NOT want the unstructured default error handler
52
+ config.error_handlers.replace([Webhookdb::Async::JobLogger.method(:error_handler)])
53
+ # We must then replace the otherwise-automatically-added sentry middleware
54
+ config.error_handlers << Amigo::RateLimitedErrorHandler.new(
55
+ Sentry::Sidekiq::ErrorHandler.new,
56
+ sample_rate: self.error_reporting_sample_rate,
57
+ ttl: self.error_reporting_ttl,
58
+ )
59
+ config.death_handlers << Webhookdb::Async::JobLogger.method(:death_handler)
60
+ config.server_middleware.add(Amigo::DurableJob::ServerMiddleware)
61
+ # We use the dead set to move jobs that we need to retry manually
62
+ config.options[:dead_max_jobs] = 999_999_999
63
+ config.server_middleware.add(Amigo::Retry::ServerMiddleware)
64
+ end
65
+
66
+ Amigo::DurableJob.failure_notifier = Webhookdb::Async::JobLogger.method(:durable_job_failure_notifier)
67
+
68
+ Sidekiq.configure_client do |config|
69
+ config.redis = redis_params
70
+ config.client_middleware.add(Amigo::DurableJob::ClientMiddleware)
71
+ end
72
+ end
73
+ end
74
+
75
+ def self.open_web
76
+ u = URI(Webhookdb.api_url)
77
+ u.user = self.web_username
78
+ u.password = self.web_password
79
+ u.path = "/sidekiq"
80
+ `open #{u}`
81
+ end
82
+
83
+ # Set up async for the web/client side of things.
84
+ # This performs common Amigo config,
85
+ # and sets up the routing/auditing jobs.
86
+ #
87
+ # Note that we must also require all async jobs,
88
+ # since in some cases we may have sidekiq middleware that needs
89
+ # access to the actual job class, so it must be available.
90
+ def self.setup_web
91
+ self._setup_common
92
+ Amigo.install_amigo_jobs
93
+ self._require_jobs
94
+ return true
95
+ end
96
+
97
+ # Set up the worker process.
98
+ # This peforms common Amigo config,
99
+ # sets up the routing/audit jobs (since jobs may publish to other jobs),
100
+ # requires the actual jobs,
101
+ # and starts the cron.
102
+ def self.setup_workers
103
+ self._setup_common
104
+ Amigo.install_amigo_jobs
105
+ self._require_jobs
106
+ Amigo.start_scheduler
107
+ return true
108
+ end
109
+
110
+ # Set up for tests.
111
+ # This performs common config and requires the jobs.
112
+ # It does not install the routing/auditing jobs,
113
+ # since those should only be installed at specific times.
114
+ def self.setup_tests
115
+ return if Amigo.structured_logging # assume we are set up
116
+ self._setup_common
117
+ self._require_jobs
118
+ return true
119
+ end
120
+
121
+ def self._require_jobs
122
+ Amigo::DurableJob.replace_database_settings(
123
+ loggers: [Webhookdb.logger],
124
+ **Webhookdb::Dbutil.configured_connection_options,
125
+ )
126
+ Gem.find_files(File.join("webhookdb/jobs/*.rb")).each do |path|
127
+ require path
128
+ end
129
+ end
130
+
131
+ def self._setup_common
132
+ raise "Async already setup, only call this once" if Amigo.structured_logging
133
+ Amigo.structured_logging = true
134
+ Amigo.log_callback = lambda { |j, lvl, msg, o|
135
+ lg = j ? Appydays::Loggable[j] : Webhookdb::Async::JobLogger.logger
136
+ lg.send(lvl, msg, o)
137
+ }
138
+ end
139
+ end
140
+
141
+ require "webhookdb/async/audit_logger"
142
+ Amigo.audit_logger_class = Webhookdb::Async::AuditLogger
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aws-sdk-core"
4
+ require "aws-sdk-sts"
5
+ require "appydays/configurable"
6
+
7
+ require "webhookdb" unless defined?(Webhookdb)
8
+
9
+ module Webhookdb::AWS
10
+ extend Webhookdb::MethodUtilities
11
+ include Appydays::Configurable
12
+ include Appydays::Loggable
13
+
14
+ class ShimLogger
15
+ def initialize(logger, operation_prefix: "aws_", level: :debug, error_level: :warn)
16
+ @logger = logger
17
+ @param_formatter = ::Aws::Log::ParamFormatter.new({})
18
+ @param_filter = ::Aws::Log::ParamFilter.new({})
19
+ @operation_prefix = operation_prefix
20
+ @level = level
21
+ @error_level = error_level
22
+ end
23
+
24
+ # Logger method. Receives the unformatted response form the shim formatter,
25
+ # and logs a structured log.
26
+ # @param [Seahorse::Client::Response] response
27
+ def logshim(response)
28
+ level = @level
29
+ msg = @operation_prefix + response.context.operation_name.to_s
30
+ params = response.context.params
31
+ type = response.context.operation.input.shape.struct_class
32
+ ctx = {
33
+ aws_client: (response.context.client.class.name || "").delete_prefix("Aws::").delete_suffix("::Client"),
34
+ http_response_code: response.context.http_response.status_code,
35
+ elapsed: response.context[:logging_completed_at] - response.context[:logging_started_at],
36
+ request_params: @param_formatter.summarize(@param_filter.filter(params, type)),
37
+ }
38
+ ctx[:retries] = response.context.retries if response.context.retries.positive?
39
+ if response.error
40
+ level = @error_level
41
+ ctx[:error_class] = response.error.class.name
42
+ ctx[:error_message] = response.error.message
43
+ end
44
+ @logger.send(level, msg, ctx)
45
+ end
46
+ end
47
+
48
+ class ShimFormatter
49
+ # Normally this must return a string, but we need to have it return a raw response
50
+ # for use in the shim logger.
51
+ # @param [Seahorse::Client::Response] response
52
+ # @return [String]
53
+ def format(response)
54
+ # noinspection RubyMismatchedReturnType
55
+ return response
56
+ end
57
+ end
58
+
59
+ singleton_attr_reader :sts_client
60
+
61
+ configurable(:aws) do
62
+ # WebhookDB's AWS Account ID.
63
+ # Used for things like cross-account role assumption.
64
+ setting :external_account_id, "054088425385"
65
+
66
+ # Some stuff doesn't work right with explicit config, so force-set the ENV
67
+ setting :access_key_id,
68
+ "default-access",
69
+ key: "AWS_ACCESS_KEY_ID",
70
+ side_effect: ->(v) { ENV["AWS_ACCESS_KEY_ID"] = v }
71
+ setting :secret_access_key,
72
+ "default-secret",
73
+ key: "AWS_SECRET_ACCESS_KEY",
74
+ side_effect: ->(v) { ENV["AWS_SECRET_ACCESS_KEY"] = v }
75
+ setting :region,
76
+ "us-west-2",
77
+ key: "AWS_REGION",
78
+ side_effect: ->(v) { ENV["AWS_REGION"] = v }
79
+ setting :http_timeout, 30
80
+
81
+ after_configured do
82
+ ::Aws.config.update(
83
+ logger: ShimLogger.new(self.logger),
84
+ log_formatter: ShimFormatter.new,
85
+ log_level: :logshim,
86
+ )
87
+
88
+ @sts_client = Aws::STS::Client.new
89
+ end
90
+ end
91
+
92
+ REGIONS_TO_LOCATIONS = Aws.partitions.each_with_object({}) do |partition, h|
93
+ partition.regions.each do |region|
94
+ h[region.name] = region.description
95
+ end
96
+ end.freeze
97
+ LOCATIONS_TO_REGIONS = REGIONS_TO_LOCATIONS.to_a.to_h(&:reverse!).freeze
98
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Represents the boundaries around a single execution of backfilling an integration.
4
+ # Each instance points to a single run of a single integration.
5
+ # There may be child jobs pointing to dependent integrations,
6
+ # or a parent job for a dependency backfill.
7
+ #
8
+ # When creating jobs, you can create a single job (a 'shallow' backfill)
9
+ # with +create+, or use +create_recursive+ to create jobs for all dependencies
10
+ # (a 'job group').
11
+ #
12
+ # Each job tracks when the backfill starts and ends.
13
+ # Iterating the full job graph can determine if a group is fully finished,
14
+ # or still in-progress.
15
+ #
16
+ class Webhookdb::BackfillJob < Webhookdb::Postgres::Model(:backfill_jobs)
17
+ plugin :timestamps
18
+
19
+ many_to_one :service_integration, class: "Webhookdb::ServiceIntegration"
20
+ many_to_one :parent_job, class: "Webhookdb::BackfillJob"
21
+ one_to_many :child_jobs, class: "Webhookdb::BackfillJob", key: :parent_job_id
22
+ many_to_one :created_by, class: "Webhookdb::Customer"
23
+
24
+ attr_accessor :_fixture_cascade
25
+
26
+ # @return [Webhookdb::BackfillJob]
27
+ def self.create_recursive(service_integration:, incremental:, created_by: nil, parent_job: nil, criteria: nil)
28
+ self.db.transaction do
29
+ root = self.create(service_integration:, parent_job:, incremental:, created_by:, criteria: criteria || {})
30
+ root.setup_recursive
31
+ root
32
+ end
33
+ end
34
+
35
+ # You should use ::create_recursive instead.
36
+ # This is mostly here for use in tests/fixtures.
37
+ def setup_recursive
38
+ raise Webhookdb::InvalidPrecondition, "already has children" if self.child_jobs.present?
39
+ self.service_integration.dependents.map do |dep|
40
+ self.class.create_recursive(service_integration: dep, parent_job: self, incremental:, criteria:)
41
+ end
42
+ end
43
+
44
+ def incremental? = self.incremental
45
+
46
+ def started? = !!self.started_at
47
+ def finished? = !!self.finished_at
48
+
49
+ def fully_finished_at
50
+ parent_finished = self.finished_at
51
+ return nil if parent_finished.nil?
52
+ children_finished = self.child_jobs.map(&:fully_finished_at)
53
+ return nil if children_finished.any?(&:nil?)
54
+ children_finished << parent_finished
55
+ return children_finished.max
56
+ end
57
+
58
+ def fully_finished? = !!self.fully_finished_at
59
+
60
+ def status
61
+ return "enqueued" unless self.started?
62
+ return "finished" if self.fully_finished?
63
+ return "inprogress"
64
+ end
65
+
66
+ def enqueue
67
+ self.publish_deferred("run", self.id)
68
+ end
69
+
70
+ def enqueue_children
71
+ self.child_jobs.each(&:enqueue)
72
+ end
73
+
74
+ #
75
+ # :section: Sequel Hooks
76
+ #
77
+
78
+ def before_create
79
+ self[:opaque_id] ||= Webhookdb::Id.new_opaque_id("bfj")
80
+ end
81
+ end
82
+
83
+ # Table: backfill_jobs
84
+ # ---------------------------------------------------------------------------------------------------------------------------
85
+ # Columns:
86
+ # id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
87
+ # created_at | timestamp with time zone | NOT NULL DEFAULT now()
88
+ # updated_at | timestamp with time zone |
89
+ # started_at | timestamp with time zone |
90
+ # finished_at | timestamp with time zone |
91
+ # opaque_id | text | NOT NULL
92
+ # service_integration_id | integer | NOT NULL
93
+ # parent_job_id | integer |
94
+ # created_by_id | integer |
95
+ # incremental | boolean | NOT NULL
96
+ # Indexes:
97
+ # backfill_jobs_pkey | PRIMARY KEY btree (id)
98
+ # backfill_jobs_opaque_id_key | UNIQUE btree (opaque_id)
99
+ # backfill_jobs_parent_job_id_index | btree (parent_job_id)
100
+ # backfill_jobs_service_integration_id_index | btree (service_integration_id)
101
+ # Foreign key constraints:
102
+ # backfill_jobs_created_by_id_fkey | (created_by_id) REFERENCES customers(id) ON DELETE SET NULL
103
+ # backfill_jobs_parent_job_id_fkey | (parent_job_id) REFERENCES backfill_jobs(id)
104
+ # backfill_jobs_service_integration_id_fkey | (service_integration_id) REFERENCES service_integrations(id) ON DELETE CASCADE
105
+ # Referenced By:
106
+ # backfill_jobs | backfill_jobs_parent_job_id_fkey | (parent_job_id) REFERENCES backfill_jobs(id)
107
+ # ---------------------------------------------------------------------------------------------------------------------------
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Implementation of a generic backfill pattern.
4
+ class Webhookdb::Backfiller
5
+ # Called for each item.
6
+ def handle_item(item)
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def fetch_backfill_page(pagination_token, last_backfilled:)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ # Use nil last_backfilled for a full sync, pass it for an incremental.
15
+ # Should be service integration last_backfilled_at, the timestamp of
16
+ # the latest resource, etc.
17
+ def backfill(last_backfilled)
18
+ pagination_token = nil
19
+ loop do
20
+ page, next_pagination_token = self._fetch_backfill_page_with_retry(
21
+ pagination_token, last_backfilled:,
22
+ )
23
+ if page.nil?
24
+ msg = "Fetching a page should return an empty array, not nil. The service response probably is missing a key?"
25
+ raise TypeError, msg
26
+ end
27
+ pagination_token = next_pagination_token
28
+ page.each do |item|
29
+ self.handle_item(item)
30
+ end
31
+ Amigo::DurableJob.heartbeat
32
+ break if pagination_token.blank?
33
+ if Webhookdb.regression_mode?
34
+ Webhookdb.logger.warn("regression_mode_backfill_termination", backfiller: self.to_s, pagination_token:)
35
+ break
36
+ end
37
+ end
38
+ self.flush_pending_inserts if self.respond_to?(:flush_pending_inserts)
39
+ end
40
+
41
+ def max_backfill_retry_attempts
42
+ return 3
43
+ end
44
+
45
+ def wait_for_retry_attempt(attempt:)
46
+ Webhookdb::Backfiller.do_retry_wait(attempt)
47
+ end
48
+
49
+ # Make this easy to mock
50
+ def self.do_retry_wait(seconds)
51
+ Kernel.sleep(seconds)
52
+ end
53
+
54
+ def _fetch_backfill_page_with_retry(pagination_token, last_backfilled: nil, attempt: 1)
55
+ return self.fetch_backfill_page(pagination_token, last_backfilled:)
56
+ rescue Webhookdb::Http::BaseError => e
57
+ raise e if attempt >= self.max_backfill_retry_attempts
58
+ # Assume we'll never succeed on a 401, so don't bother retrying.
59
+ raise e if e.is_a?(Webhookdb::Http::Error) && e.status == 401
60
+ self.wait_for_retry_attempt(attempt:)
61
+ return self._fetch_backfill_page_with_retry(pagination_token, last_backfilled:, attempt: attempt + 1)
62
+ end
63
+
64
+ module Bulk
65
+ def upsert_page_size = raise NotImplementedError("how many items should be upserted at a time")
66
+ def prepare_body(_body) = raise NotImplementedError("add/remove keys from body before upsert")
67
+ def upserting_replicator = raise NotImplementedError("the replicator being upserted")
68
+ def remote_key_column_name = @remote_key_column_name ||= self.upserting_replicator._remote_key_column.name
69
+
70
+ def pending_inserts = @pending_inserts ||= {}
71
+ # Should `_update_where_expr` be used or not?
72
+ # Default false, since most bulk upserting is backfill,
73
+ # which should only involve upserting new rows anyway.
74
+ def conditional_upsert? = false
75
+
76
+ def dry_run? = false
77
+
78
+ # Add the item to pending upserts, and run the page upsert if needed.
79
+ # Return the key, and the item being upserted.
80
+ # @return [Array(String, Hash),Array(nil)]
81
+ def handle_item(body)
82
+ self.prepare_body(body)
83
+ inserting = self.upserting_replicator.upsert_webhook_body(body, upsert: false)
84
+ return nil, nil if inserting.nil?
85
+ k = inserting.fetch(self.remote_key_column_name)
86
+ self.pending_inserts[k] = inserting
87
+ self.flush_pending_inserts if self.pending_inserts.size >= self.upsert_page_size
88
+ return k, inserting
89
+ end
90
+
91
+ def flush_pending_inserts
92
+ return if self.dry_run?
93
+ return if self.pending_inserts.empty?
94
+ rows_to_insert = self.pending_inserts.values
95
+ update_where = self.conditional_upsert? ? self.upserting_replicator._update_where_expr : nil
96
+ self.upserting_replicator.admin_dataset(timeout: :fast) do |ds|
97
+ insert_ds = ds.insert_conflict(
98
+ target: self.upserting_replicator._remote_key_column.name,
99
+ update: self.upserting_replicator._upsert_update_expr(rows_to_insert.first),
100
+ update_where:,
101
+ )
102
+ insert_ds.multi_insert(rows_to_insert)
103
+ end
104
+ self.pending_inserts.clear
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "appydays/configurable"
4
+ require "appydays/loggable"
5
+ require "oj"
6
+
7
+ class Webhookdb::Cloudflare
8
+ include Appydays::Configurable
9
+ include Appydays::Loggable
10
+
11
+ configurable(:cloudflare) do
12
+ setting :api_token, "set-me-to-token"
13
+ setting :host, "https://api.cloudflare.com"
14
+ end
15
+
16
+ def self.headers
17
+ return {
18
+ "Authorization" => "Bearer #{self.api_token}",
19
+ }
20
+ end
21
+
22
+ # https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record
23
+ def self.create_zone_dns_record(name:, content:, zone_id:, type: "CNAME", ttl: 1)
24
+ body = {
25
+ type:,
26
+ name:,
27
+ content:,
28
+ ttl:,
29
+ }
30
+ response = Webhookdb::Http.post(
31
+ self.host + "/client/v4/zones/#{zone_id}/dns_records",
32
+ body,
33
+ headers: self.headers,
34
+ logger: self.logger,
35
+ timeout: nil,
36
+ )
37
+ return Oj.load(response.body)
38
+ end
39
+ end