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.
- checksums.yaml +7 -0
- data/data/messages/layouts/blank.email.liquid +10 -0
- data/data/messages/layouts/minimal.email.liquid +28 -0
- data/data/messages/layouts/standard.email.liquid +28 -0
- data/data/messages/partials/button.liquid +15 -0
- data/data/messages/partials/environment_banner.liquid +9 -0
- data/data/messages/partials/footer.liquid +22 -0
- data/data/messages/partials/greeting.liquid +3 -0
- data/data/messages/partials/logo_header.liquid +18 -0
- data/data/messages/partials/signoff.liquid +1 -0
- data/data/messages/styles/v1.liquid +346 -0
- data/data/messages/templates/errors/icalendar_fetch.email.liquid +29 -0
- data/data/messages/templates/invite.email.liquid +15 -0
- data/data/messages/templates/new_customer.email.liquid +24 -0
- data/data/messages/templates/org_database_migration_finished.email.liquid +7 -0
- data/data/messages/templates/org_database_migration_started.email.liquid +9 -0
- data/data/messages/templates/specs/_field_partial.liquid +1 -0
- data/data/messages/templates/specs/basic.email.liquid +2 -0
- data/data/messages/templates/specs/basic.fake.liquid +1 -0
- data/data/messages/templates/specs/with_field.email.liquid +2 -0
- data/data/messages/templates/specs/with_field.fake.liquid +1 -0
- data/data/messages/templates/specs/with_include.email.liquid +2 -0
- data/data/messages/templates/specs/with_partial.email.liquid +1 -0
- data/data/messages/templates/verification.email.liquid +14 -0
- data/data/messages/templates/verification.sms.liquid +1 -0
- data/data/messages/web/install-customer-login.liquid +48 -0
- data/data/messages/web/install-error.liquid +17 -0
- data/data/messages/web/install-success.liquid +35 -0
- data/data/messages/web/install.liquid +20 -0
- data/data/messages/web/partials/footer.liquid +4 -0
- data/data/messages/web/partials/form_error.liquid +1 -0
- data/data/messages/web/partials/header.liquid +3 -0
- data/data/messages/web/styles.liquid +134 -0
- data/data/windows_tz.txt +461 -0
- data/db/migrations/001_testing_pixies.rb +13 -0
- data/db/migrations/002_initial.rb +132 -0
- data/db/migrations/003_ux_overhaul.rb +20 -0
- data/db/migrations/004_incremental_backfill.rb +9 -0
- data/db/migrations/005_log_webhooks.rb +24 -0
- data/db/migrations/006_generalize_roles.rb +29 -0
- data/db/migrations/007_org_dns.rb +12 -0
- data/db/migrations/008_webhook_subscriptions.rb +19 -0
- data/db/migrations/009_nonunique_stripe_subscription_customer.rb +16 -0
- data/db/migrations/010_drop_integration_soft_delete.rb +14 -0
- data/db/migrations/011_webhook_subscriptions_created_at.rb +10 -0
- data/db/migrations/012_webhook_subscriptions_created_by.rb +9 -0
- data/db/migrations/013_default_org_membership.rb +30 -0
- data/db/migrations/014_webhook_subscription_deliveries.rb +26 -0
- data/db/migrations/015_dependent_integrations.rb +9 -0
- data/db/migrations/016_encrypted_columns.rb +9 -0
- data/db/migrations/017_skip_verification.rb +9 -0
- data/db/migrations/018_sync_targets.rb +25 -0
- data/db/migrations/019_org_schema.rb +9 -0
- data/db/migrations/020_org_database_migrations.rb +25 -0
- data/db/migrations/021_no_default_org_schema.rb +14 -0
- data/db/migrations/022_database_document.rb +15 -0
- data/db/migrations/023_sync_target_schema.rb +9 -0
- data/db/migrations/024_org_semaphore_jobs.rb +9 -0
- data/db/migrations/025_integration_backfill_cursor.rb +9 -0
- data/db/migrations/026_undo_integration_backfill_cursor.rb +9 -0
- data/db/migrations/027_sync_target_http_sync.rb +12 -0
- data/db/migrations/028_logged_webhook_path.rb +24 -0
- data/db/migrations/029_encrypt_columns.rb +97 -0
- data/db/migrations/030_org_sync_target_timeout.rb +9 -0
- data/db/migrations/031_org_max_query_rows.rb +9 -0
- data/db/migrations/032_remove_db_defaults.rb +12 -0
- data/db/migrations/033_backfill_jobs.rb +26 -0
- data/db/migrations/034_backfill_job_criteria.rb +9 -0
- data/db/migrations/035_synchronous_backfill.rb +9 -0
- data/db/migrations/036_oauth.rb +26 -0
- data/db/migrations/037_oauth_used.rb +9 -0
- data/lib/amigo/durable_job.rb +416 -0
- data/lib/pry/clipboard.rb +111 -0
- data/lib/sequel/advisory_lock.rb +65 -0
- data/lib/webhookdb/admin.rb +4 -0
- data/lib/webhookdb/admin_api/auth.rb +36 -0
- data/lib/webhookdb/admin_api/customers.rb +63 -0
- data/lib/webhookdb/admin_api/database_documents.rb +20 -0
- data/lib/webhookdb/admin_api/entities.rb +66 -0
- data/lib/webhookdb/admin_api/message_deliveries.rb +61 -0
- data/lib/webhookdb/admin_api/roles.rb +15 -0
- data/lib/webhookdb/admin_api.rb +34 -0
- data/lib/webhookdb/aggregate_result.rb +63 -0
- data/lib/webhookdb/api/auth.rb +122 -0
- data/lib/webhookdb/api/connstr_auth.rb +36 -0
- data/lib/webhookdb/api/db.rb +188 -0
- data/lib/webhookdb/api/demo.rb +14 -0
- data/lib/webhookdb/api/entities.rb +198 -0
- data/lib/webhookdb/api/helpers.rb +253 -0
- data/lib/webhookdb/api/install.rb +296 -0
- data/lib/webhookdb/api/me.rb +53 -0
- data/lib/webhookdb/api/organizations.rb +254 -0
- data/lib/webhookdb/api/replay.rb +64 -0
- data/lib/webhookdb/api/service_integrations.rb +402 -0
- data/lib/webhookdb/api/services.rb +27 -0
- data/lib/webhookdb/api/stripe.rb +22 -0
- data/lib/webhookdb/api/subscriptions.rb +67 -0
- data/lib/webhookdb/api/sync_targets.rb +232 -0
- data/lib/webhookdb/api/system.rb +37 -0
- data/lib/webhookdb/api/webhook_subscriptions.rb +96 -0
- data/lib/webhookdb/api.rb +92 -0
- data/lib/webhookdb/apps.rb +93 -0
- data/lib/webhookdb/async/audit_logger.rb +38 -0
- data/lib/webhookdb/async/autoscaler.rb +84 -0
- data/lib/webhookdb/async/job.rb +18 -0
- data/lib/webhookdb/async/job_logger.rb +45 -0
- data/lib/webhookdb/async/scheduled_job.rb +18 -0
- data/lib/webhookdb/async.rb +142 -0
- data/lib/webhookdb/aws.rb +98 -0
- data/lib/webhookdb/backfill_job.rb +107 -0
- data/lib/webhookdb/backfiller.rb +107 -0
- data/lib/webhookdb/cloudflare.rb +39 -0
- data/lib/webhookdb/connection_cache.rb +177 -0
- data/lib/webhookdb/console.rb +71 -0
- data/lib/webhookdb/convertkit.rb +14 -0
- data/lib/webhookdb/crypto.rb +66 -0
- data/lib/webhookdb/customer/reset_code.rb +94 -0
- data/lib/webhookdb/customer.rb +347 -0
- data/lib/webhookdb/database_document.rb +72 -0
- data/lib/webhookdb/db_adapter/column_types.rb +37 -0
- data/lib/webhookdb/db_adapter/default_sql.rb +187 -0
- data/lib/webhookdb/db_adapter/pg.rb +96 -0
- data/lib/webhookdb/db_adapter/snowflake.rb +137 -0
- data/lib/webhookdb/db_adapter.rb +208 -0
- data/lib/webhookdb/dbutil.rb +92 -0
- data/lib/webhookdb/demo_mode.rb +100 -0
- data/lib/webhookdb/developer_alert.rb +51 -0
- data/lib/webhookdb/email_octopus.rb +21 -0
- data/lib/webhookdb/enumerable.rb +18 -0
- data/lib/webhookdb/fixtures/backfill_jobs.rb +72 -0
- data/lib/webhookdb/fixtures/customers.rb +65 -0
- data/lib/webhookdb/fixtures/database_documents.rb +27 -0
- data/lib/webhookdb/fixtures/faker.rb +41 -0
- data/lib/webhookdb/fixtures/logged_webhooks.rb +56 -0
- data/lib/webhookdb/fixtures/message_deliveries.rb +59 -0
- data/lib/webhookdb/fixtures/oauth_sessions.rb +24 -0
- data/lib/webhookdb/fixtures/organization_database_migrations.rb +37 -0
- data/lib/webhookdb/fixtures/organization_memberships.rb +54 -0
- data/lib/webhookdb/fixtures/organizations.rb +32 -0
- data/lib/webhookdb/fixtures/reset_codes.rb +23 -0
- data/lib/webhookdb/fixtures/service_integrations.rb +42 -0
- data/lib/webhookdb/fixtures/subscriptions.rb +33 -0
- data/lib/webhookdb/fixtures/sync_targets.rb +32 -0
- data/lib/webhookdb/fixtures/webhook_subscriptions.rb +35 -0
- data/lib/webhookdb/fixtures.rb +15 -0
- data/lib/webhookdb/formatting.rb +56 -0
- data/lib/webhookdb/front.rb +49 -0
- data/lib/webhookdb/github.rb +22 -0
- data/lib/webhookdb/google_calendar.rb +29 -0
- data/lib/webhookdb/heroku.rb +21 -0
- data/lib/webhookdb/http.rb +114 -0
- data/lib/webhookdb/icalendar.rb +17 -0
- data/lib/webhookdb/id.rb +17 -0
- data/lib/webhookdb/idempotency.rb +90 -0
- data/lib/webhookdb/increase.rb +42 -0
- data/lib/webhookdb/intercom.rb +23 -0
- data/lib/webhookdb/jobs/amigo_test_jobs.rb +118 -0
- data/lib/webhookdb/jobs/backfill.rb +32 -0
- data/lib/webhookdb/jobs/create_mirror_table.rb +18 -0
- data/lib/webhookdb/jobs/create_stripe_customer.rb +17 -0
- data/lib/webhookdb/jobs/customer_created_notify_internal.rb +22 -0
- data/lib/webhookdb/jobs/demo_mode_sync_data.rb +19 -0
- data/lib/webhookdb/jobs/deprecated_jobs.rb +19 -0
- data/lib/webhookdb/jobs/developer_alert_handle.rb +14 -0
- data/lib/webhookdb/jobs/durable_job_recheck_poller.rb +17 -0
- data/lib/webhookdb/jobs/emailer.rb +15 -0
- data/lib/webhookdb/jobs/icalendar_enqueue_syncs.rb +25 -0
- data/lib/webhookdb/jobs/icalendar_sync.rb +23 -0
- data/lib/webhookdb/jobs/logged_webhook_replay.rb +17 -0
- data/lib/webhookdb/jobs/logged_webhook_resilient_replay.rb +15 -0
- data/lib/webhookdb/jobs/message_dispatched.rb +16 -0
- data/lib/webhookdb/jobs/organization_database_migration_notify_finished.rb +21 -0
- data/lib/webhookdb/jobs/organization_database_migration_notify_started.rb +21 -0
- data/lib/webhookdb/jobs/organization_database_migration_run.rb +24 -0
- data/lib/webhookdb/jobs/prepare_database_connections.rb +22 -0
- data/lib/webhookdb/jobs/process_webhook.rb +47 -0
- data/lib/webhookdb/jobs/renew_watch_channel.rb +24 -0
- data/lib/webhookdb/jobs/replication_migration.rb +24 -0
- data/lib/webhookdb/jobs/reset_code_create_dispatch.rb +23 -0
- data/lib/webhookdb/jobs/scheduled_backfills.rb +77 -0
- data/lib/webhookdb/jobs/send_invite.rb +15 -0
- data/lib/webhookdb/jobs/send_test_webhook.rb +25 -0
- data/lib/webhookdb/jobs/send_webhook.rb +20 -0
- data/lib/webhookdb/jobs/sync_target_enqueue_scheduled.rb +16 -0
- data/lib/webhookdb/jobs/sync_target_run_sync.rb +38 -0
- data/lib/webhookdb/jobs/trim_logged_webhooks.rb +15 -0
- data/lib/webhookdb/jobs/webhook_resource_notify_integrations.rb +30 -0
- data/lib/webhookdb/jobs/webhook_subscription_delivery_attempt.rb +29 -0
- data/lib/webhookdb/jobs.rb +4 -0
- data/lib/webhookdb/json.rb +113 -0
- data/lib/webhookdb/liquid/expose.rb +27 -0
- data/lib/webhookdb/liquid/filters.rb +16 -0
- data/lib/webhookdb/liquid/liquification.rb +26 -0
- data/lib/webhookdb/liquid/partial.rb +12 -0
- data/lib/webhookdb/logged_webhook/resilient.rb +95 -0
- data/lib/webhookdb/logged_webhook.rb +194 -0
- data/lib/webhookdb/message/body.rb +25 -0
- data/lib/webhookdb/message/delivery.rb +127 -0
- data/lib/webhookdb/message/email_transport.rb +133 -0
- data/lib/webhookdb/message/fake_transport.rb +54 -0
- data/lib/webhookdb/message/liquid_drops.rb +29 -0
- data/lib/webhookdb/message/template.rb +89 -0
- data/lib/webhookdb/message/transport.rb +43 -0
- data/lib/webhookdb/message.rb +150 -0
- data/lib/webhookdb/messages/error_icalendar_fetch.rb +42 -0
- data/lib/webhookdb/messages/invite.rb +23 -0
- data/lib/webhookdb/messages/new_customer.rb +14 -0
- data/lib/webhookdb/messages/org_database_migration_finished.rb +23 -0
- data/lib/webhookdb/messages/org_database_migration_started.rb +24 -0
- data/lib/webhookdb/messages/specs.rb +57 -0
- data/lib/webhookdb/messages/verification.rb +23 -0
- data/lib/webhookdb/method_utilities.rb +82 -0
- data/lib/webhookdb/microsoft_calendar.rb +36 -0
- data/lib/webhookdb/nextpax.rb +14 -0
- data/lib/webhookdb/oauth/front.rb +58 -0
- data/lib/webhookdb/oauth/intercom.rb +58 -0
- data/lib/webhookdb/oauth/session.rb +24 -0
- data/lib/webhookdb/oauth.rb +80 -0
- data/lib/webhookdb/organization/alerting.rb +35 -0
- data/lib/webhookdb/organization/database_migration.rb +151 -0
- data/lib/webhookdb/organization/db_builder.rb +429 -0
- data/lib/webhookdb/organization.rb +506 -0
- data/lib/webhookdb/organization_membership.rb +58 -0
- data/lib/webhookdb/phone_number.rb +38 -0
- data/lib/webhookdb/plaid.rb +23 -0
- data/lib/webhookdb/platform.rb +27 -0
- data/lib/webhookdb/plivo.rb +52 -0
- data/lib/webhookdb/postgres/maintenance.rb +166 -0
- data/lib/webhookdb/postgres/model.rb +82 -0
- data/lib/webhookdb/postgres/model_utilities.rb +382 -0
- data/lib/webhookdb/postgres/testing_pixie.rb +16 -0
- data/lib/webhookdb/postgres/validations.rb +46 -0
- data/lib/webhookdb/postgres.rb +176 -0
- data/lib/webhookdb/postmark.rb +20 -0
- data/lib/webhookdb/redis.rb +35 -0
- data/lib/webhookdb/replicator/atom_single_feed_v1.rb +116 -0
- data/lib/webhookdb/replicator/aws_pricing_v1.rb +488 -0
- data/lib/webhookdb/replicator/base.rb +1185 -0
- data/lib/webhookdb/replicator/column.rb +482 -0
- data/lib/webhookdb/replicator/convertkit_broadcast_v1.rb +69 -0
- data/lib/webhookdb/replicator/convertkit_subscriber_v1.rb +200 -0
- data/lib/webhookdb/replicator/convertkit_tag_v1.rb +66 -0
- data/lib/webhookdb/replicator/convertkit_v1_mixin.rb +65 -0
- data/lib/webhookdb/replicator/docgen.rb +167 -0
- data/lib/webhookdb/replicator/email_octopus_campaign_v1.rb +84 -0
- data/lib/webhookdb/replicator/email_octopus_contact_v1.rb +159 -0
- data/lib/webhookdb/replicator/email_octopus_event_v1.rb +244 -0
- data/lib/webhookdb/replicator/email_octopus_list_v1.rb +101 -0
- data/lib/webhookdb/replicator/fake.rb +453 -0
- data/lib/webhookdb/replicator/front_conversation_v1.rb +45 -0
- data/lib/webhookdb/replicator/front_marketplace_root_v1.rb +55 -0
- data/lib/webhookdb/replicator/front_message_v1.rb +45 -0
- data/lib/webhookdb/replicator/front_v1_mixin.rb +22 -0
- data/lib/webhookdb/replicator/github_issue_comment_v1.rb +58 -0
- data/lib/webhookdb/replicator/github_issue_v1.rb +83 -0
- data/lib/webhookdb/replicator/github_pull_v1.rb +84 -0
- data/lib/webhookdb/replicator/github_release_v1.rb +47 -0
- data/lib/webhookdb/replicator/github_repo_v1_mixin.rb +250 -0
- data/lib/webhookdb/replicator/github_repository_event_v1.rb +45 -0
- data/lib/webhookdb/replicator/icalendar_calendar_v1.rb +465 -0
- data/lib/webhookdb/replicator/icalendar_event_v1.rb +334 -0
- data/lib/webhookdb/replicator/increase_account_number_v1.rb +77 -0
- data/lib/webhookdb/replicator/increase_account_transfer_v1.rb +61 -0
- data/lib/webhookdb/replicator/increase_account_v1.rb +63 -0
- data/lib/webhookdb/replicator/increase_ach_transfer_v1.rb +78 -0
- data/lib/webhookdb/replicator/increase_check_transfer_v1.rb +64 -0
- data/lib/webhookdb/replicator/increase_limit_v1.rb +78 -0
- data/lib/webhookdb/replicator/increase_transaction_v1.rb +74 -0
- data/lib/webhookdb/replicator/increase_v1_mixin.rb +121 -0
- data/lib/webhookdb/replicator/increase_wire_transfer_v1.rb +61 -0
- data/lib/webhookdb/replicator/intercom_contact_v1.rb +36 -0
- data/lib/webhookdb/replicator/intercom_conversation_v1.rb +38 -0
- data/lib/webhookdb/replicator/intercom_marketplace_root_v1.rb +69 -0
- data/lib/webhookdb/replicator/intercom_v1_mixin.rb +105 -0
- data/lib/webhookdb/replicator/oauth_refresh_access_token_mixin.rb +65 -0
- data/lib/webhookdb/replicator/plivo_sms_inbound_v1.rb +102 -0
- data/lib/webhookdb/replicator/postmark_inbound_message_v1.rb +94 -0
- data/lib/webhookdb/replicator/postmark_outbound_message_event_v1.rb +107 -0
- data/lib/webhookdb/replicator/schema_modification.rb +42 -0
- data/lib/webhookdb/replicator/shopify_customer_v1.rb +58 -0
- data/lib/webhookdb/replicator/shopify_order_v1.rb +64 -0
- data/lib/webhookdb/replicator/shopify_v1_mixin.rb +161 -0
- data/lib/webhookdb/replicator/signalwire_message_v1.rb +169 -0
- data/lib/webhookdb/replicator/sponsy_customer_v1.rb +54 -0
- data/lib/webhookdb/replicator/sponsy_placement_v1.rb +34 -0
- data/lib/webhookdb/replicator/sponsy_publication_v1.rb +125 -0
- data/lib/webhookdb/replicator/sponsy_slot_v1.rb +41 -0
- data/lib/webhookdb/replicator/sponsy_status_v1.rb +35 -0
- data/lib/webhookdb/replicator/sponsy_v1_mixin.rb +165 -0
- data/lib/webhookdb/replicator/state_machine_step.rb +69 -0
- data/lib/webhookdb/replicator/stripe_charge_v1.rb +77 -0
- data/lib/webhookdb/replicator/stripe_coupon_v1.rb +62 -0
- data/lib/webhookdb/replicator/stripe_customer_v1.rb +60 -0
- data/lib/webhookdb/replicator/stripe_dispute_v1.rb +77 -0
- data/lib/webhookdb/replicator/stripe_invoice_item_v1.rb +82 -0
- data/lib/webhookdb/replicator/stripe_invoice_v1.rb +116 -0
- data/lib/webhookdb/replicator/stripe_payout_v1.rb +67 -0
- data/lib/webhookdb/replicator/stripe_price_v1.rb +60 -0
- data/lib/webhookdb/replicator/stripe_product_v1.rb +60 -0
- data/lib/webhookdb/replicator/stripe_refund_v1.rb +101 -0
- data/lib/webhookdb/replicator/stripe_subscription_item_v1.rb +56 -0
- data/lib/webhookdb/replicator/stripe_subscription_v1.rb +75 -0
- data/lib/webhookdb/replicator/stripe_v1_mixin.rb +116 -0
- data/lib/webhookdb/replicator/transistor_episode_stats_v1.rb +141 -0
- data/lib/webhookdb/replicator/transistor_episode_v1.rb +169 -0
- data/lib/webhookdb/replicator/transistor_show_v1.rb +68 -0
- data/lib/webhookdb/replicator/transistor_v1_mixin.rb +65 -0
- data/lib/webhookdb/replicator/twilio_sms_v1.rb +156 -0
- data/lib/webhookdb/replicator/webhook_request.rb +5 -0
- data/lib/webhookdb/replicator/webhookdb_customer_v1.rb +74 -0
- data/lib/webhookdb/replicator.rb +224 -0
- data/lib/webhookdb/role.rb +42 -0
- data/lib/webhookdb/sentry.rb +35 -0
- data/lib/webhookdb/service/auth.rb +138 -0
- data/lib/webhookdb/service/collection.rb +91 -0
- data/lib/webhookdb/service/entities.rb +97 -0
- data/lib/webhookdb/service/helpers.rb +270 -0
- data/lib/webhookdb/service/middleware.rb +124 -0
- data/lib/webhookdb/service/types.rb +30 -0
- data/lib/webhookdb/service/validators.rb +32 -0
- data/lib/webhookdb/service/view_api.rb +63 -0
- data/lib/webhookdb/service.rb +219 -0
- data/lib/webhookdb/service_integration.rb +332 -0
- data/lib/webhookdb/shopify.rb +35 -0
- data/lib/webhookdb/signalwire.rb +13 -0
- data/lib/webhookdb/slack.rb +68 -0
- data/lib/webhookdb/snowflake.rb +90 -0
- data/lib/webhookdb/spec_helpers/async.rb +122 -0
- data/lib/webhookdb/spec_helpers/citest.rb +88 -0
- data/lib/webhookdb/spec_helpers/integration.rb +121 -0
- data/lib/webhookdb/spec_helpers/message.rb +41 -0
- data/lib/webhookdb/spec_helpers/postgres.rb +220 -0
- data/lib/webhookdb/spec_helpers/service.rb +432 -0
- data/lib/webhookdb/spec_helpers/shared_examples_for_columns.rb +56 -0
- data/lib/webhookdb/spec_helpers/shared_examples_for_replicators.rb +915 -0
- data/lib/webhookdb/spec_helpers/whdb.rb +139 -0
- data/lib/webhookdb/spec_helpers.rb +63 -0
- data/lib/webhookdb/sponsy.rb +14 -0
- data/lib/webhookdb/stripe.rb +37 -0
- data/lib/webhookdb/subscription.rb +203 -0
- data/lib/webhookdb/sync_target.rb +491 -0
- data/lib/webhookdb/tasks/admin.rb +49 -0
- data/lib/webhookdb/tasks/annotate.rb +36 -0
- data/lib/webhookdb/tasks/db.rb +82 -0
- data/lib/webhookdb/tasks/docs.rb +42 -0
- data/lib/webhookdb/tasks/fixture.rb +35 -0
- data/lib/webhookdb/tasks/message.rb +50 -0
- data/lib/webhookdb/tasks/regress.rb +87 -0
- data/lib/webhookdb/tasks/release.rb +27 -0
- data/lib/webhookdb/tasks/sidekiq.rb +23 -0
- data/lib/webhookdb/tasks/specs.rb +64 -0
- data/lib/webhookdb/theranest.rb +15 -0
- data/lib/webhookdb/transistor.rb +13 -0
- data/lib/webhookdb/twilio.rb +13 -0
- data/lib/webhookdb/typed_struct.rb +44 -0
- data/lib/webhookdb/version.rb +5 -0
- data/lib/webhookdb/webhook_response.rb +50 -0
- data/lib/webhookdb/webhook_subscription/delivery.rb +82 -0
- data/lib/webhookdb/webhook_subscription.rb +226 -0
- data/lib/webhookdb/windows_tz.rb +32 -0
- data/lib/webhookdb/xml.rb +92 -0
- data/lib/webhookdb.rb +224 -0
- data/lib/webterm/apps.rb +45 -0
- 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
|