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,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb/message/template"
|
|
4
|
+
|
|
5
|
+
class Webhookdb::Messages::NewCustomer < Webhookdb::Message::Template
|
|
6
|
+
def self.fixtured(recipient)
|
|
7
|
+
return self.new(recipient)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(customer)
|
|
11
|
+
@customer = customer
|
|
12
|
+
super()
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb/message/template"
|
|
4
|
+
|
|
5
|
+
class Webhookdb::Messages::OrgDatabaseMigrationFinished < Webhookdb::Message::Template
|
|
6
|
+
def self.fixtured(recipient)
|
|
7
|
+
dbm = Webhookdb::Fixtures.organization_database_migration.with_urls.finished.create
|
|
8
|
+
Webhookdb::Fixtures.organization_membership.org(dbm.organization).customer(recipient).verified.admin.create
|
|
9
|
+
return self.new(dbm)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(org_database_migration)
|
|
13
|
+
@org_database_migration = org_database_migration
|
|
14
|
+
super()
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def liquid_drops
|
|
18
|
+
return super.merge(
|
|
19
|
+
org_name: @org_database_migration.organization.name,
|
|
20
|
+
destination_host: @org_database_migration.displaysafe_destination_url,
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb/message/template"
|
|
4
|
+
|
|
5
|
+
class Webhookdb::Messages::OrgDatabaseMigrationStarted < Webhookdb::Message::Template
|
|
6
|
+
def self.fixtured(recipient)
|
|
7
|
+
dbm = Webhookdb::Fixtures.organization_database_migration.with_urls.started.create
|
|
8
|
+
Webhookdb::Fixtures.organization_membership.org(dbm.organization).customer(recipient).verified.admin.create
|
|
9
|
+
return self.new(dbm)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(org_database_migration)
|
|
13
|
+
@org_database_migration = org_database_migration
|
|
14
|
+
super()
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def liquid_drops
|
|
18
|
+
return super.merge(
|
|
19
|
+
org_name: @org_database_migration.organization.name,
|
|
20
|
+
source_host: @org_database_migration.displaysafe_source_url,
|
|
21
|
+
destination_host: @org_database_migration.displaysafe_destination_url,
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb/message/template"
|
|
4
|
+
|
|
5
|
+
module Webhookdb::Messages::Testers
|
|
6
|
+
class Base < Webhookdb::Message::Template
|
|
7
|
+
def template_folder
|
|
8
|
+
return "specs"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def layout
|
|
12
|
+
return nil
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Basic < Base
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class WithField < Base
|
|
20
|
+
def initialize(field)
|
|
21
|
+
@field = field
|
|
22
|
+
super()
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def liquid_drops
|
|
26
|
+
return super.merge(field: @field)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class Nonextant < Base
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class MissingField < Base
|
|
34
|
+
def template_name
|
|
35
|
+
return "with_field"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class WithInclude < Base
|
|
40
|
+
def liquid_drops
|
|
41
|
+
return super.merge(field: 3)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class WithPartial < Base
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class WithLayout < Base
|
|
49
|
+
def template_name
|
|
50
|
+
return "basic"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def layout
|
|
54
|
+
return "standard"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb/message/template"
|
|
4
|
+
|
|
5
|
+
class Webhookdb::Messages::Verification < Webhookdb::Message::Template
|
|
6
|
+
def self.fixtured(recipient)
|
|
7
|
+
code = Webhookdb::Fixtures.reset_code(customer: recipient).create
|
|
8
|
+
return self.new(code)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(reset_code)
|
|
12
|
+
@reset_code = reset_code
|
|
13
|
+
super()
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def liquid_drops
|
|
17
|
+
return super.merge(
|
|
18
|
+
expire_at: @reset_code.expire_at,
|
|
19
|
+
token: @reset_code.token,
|
|
20
|
+
email: @reset_code.customer.email,
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb" unless defined?(Webhookdb)
|
|
4
|
+
|
|
5
|
+
# A collection of methods for declaring other methods.
|
|
6
|
+
#
|
|
7
|
+
# class MyClass
|
|
8
|
+
# extend Webhookdb::MethodUtilities
|
|
9
|
+
#
|
|
10
|
+
# singleton_attr_accessor :types
|
|
11
|
+
# singleton_method_alias :kinds, :types
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# MyClass.types = [ :pheno, :proto, :stereo ]
|
|
15
|
+
# MyClass.kinds # => [:pheno, :proto, :stereo]
|
|
16
|
+
#
|
|
17
|
+
module Webhookdb::MethodUtilities
|
|
18
|
+
### Creates instance variables and corresponding methods that return their
|
|
19
|
+
### values for each of the specified +symbols+ in the singleton of the
|
|
20
|
+
### declaring object (e.g., class instance variables and methods if declared
|
|
21
|
+
### in a Class).
|
|
22
|
+
def singleton_attr_reader(*symbols)
|
|
23
|
+
singleton_class.instance_exec(symbols) do |attrs|
|
|
24
|
+
attr_reader(*attrs)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
### Create instance variables and corresponding methods that return
|
|
29
|
+
### true or false values for each of the specified +symbols+ in the singleton
|
|
30
|
+
### of the declaring object.
|
|
31
|
+
def singleton_predicate_reader(*symbols)
|
|
32
|
+
singleton_class.extend(Webhookdb::MethodUtilities)
|
|
33
|
+
singleton_class.attr_predicate(*symbols)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
### Creates methods that allow assignment to the attributes of the singleton
|
|
37
|
+
### of the declaring object that correspond to the specified +symbols+.
|
|
38
|
+
def singleton_attr_writer(*symbols)
|
|
39
|
+
singleton_class.instance_exec(symbols) do |attrs|
|
|
40
|
+
attr_writer(*attrs)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
### Creates readers and writers that allow assignment to the attributes of
|
|
45
|
+
### the singleton of the declaring object that correspond to the specified
|
|
46
|
+
### +symbols+.
|
|
47
|
+
def singleton_attr_accessor(*symbols)
|
|
48
|
+
symbols.each do |sym|
|
|
49
|
+
singleton_class.__send__(:attr_accessor, sym)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
### Create predicate methods and writers that allow assignment to the attributes
|
|
54
|
+
### of the singleton of the declaring object that correspond to the specified
|
|
55
|
+
### +symbols+.
|
|
56
|
+
def singleton_predicate_accessor(*symbols)
|
|
57
|
+
singleton_class.extend(Webhookdb::MethodUtilities)
|
|
58
|
+
singleton_class.attr_predicate_accessor(*symbols)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
### Creates an alias for the +original+ method named +newname+.
|
|
62
|
+
def singleton_method_alias(newname, original)
|
|
63
|
+
singleton_class.__send__(:alias_method, newname, original)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
### Create a reader in the form of a predicate for the given +attrname+.
|
|
67
|
+
def attr_predicate(attrname)
|
|
68
|
+
attrname = attrname.to_s.chomp("?")
|
|
69
|
+
define_method(:"#{attrname}?") do
|
|
70
|
+
instance_variable_get(:"@#{attrname}") ? true : false
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
### Create a reader in the form of a predicate for the given +attrname+
|
|
75
|
+
### as well as a regular writer method.
|
|
76
|
+
def attr_predicate_accessor(attrname)
|
|
77
|
+
attrname = attrname.to_s.chomp("?")
|
|
78
|
+
attr_writer(attrname)
|
|
79
|
+
|
|
80
|
+
attr_predicate(attrname)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "appydays/configurable"
|
|
4
|
+
|
|
5
|
+
module Webhookdb::MicrosoftCalendar
|
|
6
|
+
include Appydays::Configurable
|
|
7
|
+
extend Webhookdb::MethodUtilities
|
|
8
|
+
|
|
9
|
+
singleton_attr_accessor :calendar_view_start_time
|
|
10
|
+
singleton_attr_accessor :calendar_view_end_time
|
|
11
|
+
|
|
12
|
+
configurable(:microsoft_calendar) do
|
|
13
|
+
# How many calendars/events should we fetch in a single page?
|
|
14
|
+
# Higher uses slightly more memory but fewer API calls.
|
|
15
|
+
# Apparent maximum is 999,999,999.
|
|
16
|
+
setting :list_page_size, 500
|
|
17
|
+
# How many rows should we upsert at a time?
|
|
18
|
+
# Higher is fewer upserts, but can create very large SQL strings,
|
|
19
|
+
# which can have negative performance.
|
|
20
|
+
setting :upsert_page_size, 500
|
|
21
|
+
|
|
22
|
+
# These should be ISO8601 strings. We use them in our calls to the Microsoft Graph API
|
|
23
|
+
# to determine the timeframe we are pulling events from.
|
|
24
|
+
setting :calendar_view_start, Time.new(2022, 10, 1).iso8601
|
|
25
|
+
setting :calendar_view_end, (Time.new(2022, 10, 1) + 1825.days).iso8601
|
|
26
|
+
|
|
27
|
+
setting :http_timeout, 30
|
|
28
|
+
|
|
29
|
+
after_configured do
|
|
30
|
+
self.calendar_view_start_time = Time.parse(self.calendar_view_start)
|
|
31
|
+
self.calendar_view_end_time = Time.parse(self.calendar_view_end)
|
|
32
|
+
raise ArgumentError, "calendar view end must be after calendar view start" if
|
|
33
|
+
self.calendar_view_start_time >= self.calendar_view_end_time
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "appydays/configurable"
|
|
4
|
+
|
|
5
|
+
module Webhookdb::Nextpax
|
|
6
|
+
include Appydays::Configurable
|
|
7
|
+
|
|
8
|
+
configurable(:nextpax) do
|
|
9
|
+
setting :constants_sync_cron_expression, "0 */12 * * *"
|
|
10
|
+
setting :property_changes_cron_expression, "*/1 * * * *"
|
|
11
|
+
setting :http_timeout, 30
|
|
12
|
+
setting :page_size, 20
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb/front"
|
|
4
|
+
|
|
5
|
+
class Webhookdb::Oauth::Front < Webhookdb::Oauth::Provider
|
|
6
|
+
include Appydays::Loggable
|
|
7
|
+
|
|
8
|
+
def key = "front"
|
|
9
|
+
def app_name = "Front"
|
|
10
|
+
def requires_webhookdb_auth? = true
|
|
11
|
+
def supports_webhooks? = true
|
|
12
|
+
|
|
13
|
+
def authorization_url(state:)
|
|
14
|
+
return "https://app.frontapp.com/oauth/authorize?response_type=code&redirect_uri=#{Webhookdb::Front.oauth_callback_url}&state=#{state}&client_id=#{Webhookdb::Front.client_id}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def exchange_authorization_code(code:)
|
|
18
|
+
token = Webhookdb::Http.post(
|
|
19
|
+
"https://app.frontapp.com/oauth/token",
|
|
20
|
+
{
|
|
21
|
+
"code" => code,
|
|
22
|
+
"redirect_uri" => Webhookdb::Front.oauth_callback_url,
|
|
23
|
+
"grant_type" => "authorization_code",
|
|
24
|
+
},
|
|
25
|
+
logger: self.logger,
|
|
26
|
+
timeout: Webhookdb::Front.http_timeout,
|
|
27
|
+
basic_auth: {username: Webhookdb::Front.client_id, password: Webhookdb::Front.client_secret},
|
|
28
|
+
)
|
|
29
|
+
return Webhookdb::Oauth::Tokens.new(
|
|
30
|
+
access_token: token.parsed_response["access_token"],
|
|
31
|
+
refresh_token: token.parsed_response["refresh_token"],
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def build_marketplace_integrations(organization:, tokens:, **)
|
|
36
|
+
# I asked the dev team at front specifically how to differentiate between instances when receiving webhooks,
|
|
37
|
+
# and they said to look at the root url of the link provided for the resource in every response. In order to
|
|
38
|
+
# retrieve that value for the integrations that we'll be finding or creating, we look at this token info
|
|
39
|
+
# response.
|
|
40
|
+
front_token_info_resp = Webhookdb::Http.get(
|
|
41
|
+
"https://api2.frontapp.com/me",
|
|
42
|
+
headers: Webhookdb::Front.auth_headers(tokens.access_token),
|
|
43
|
+
logger: self.logger,
|
|
44
|
+
timeout: Webhookdb::Front.http_timeout,
|
|
45
|
+
)
|
|
46
|
+
front_token_info = front_token_info_resp.parsed_response
|
|
47
|
+
resource_url = front_token_info.dig("_links", "self")
|
|
48
|
+
instance_root_url = resource_url.nil? ? nil : URI.parse(resource_url).host
|
|
49
|
+
|
|
50
|
+
root_sint = Webhookdb::ServiceIntegration.create_disambiguated(
|
|
51
|
+
"front_marketplace_root_v1",
|
|
52
|
+
organization:,
|
|
53
|
+
api_url: instance_root_url,
|
|
54
|
+
backfill_key: tokens.refresh_token,
|
|
55
|
+
)
|
|
56
|
+
root_sint.replicator.build_dependents
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb/intercom"
|
|
4
|
+
|
|
5
|
+
class Webhookdb::Oauth::Intercom < Webhookdb::Oauth::Provider
|
|
6
|
+
include Appydays::Loggable
|
|
7
|
+
|
|
8
|
+
def key = "intercom"
|
|
9
|
+
def app_name = "Intercom"
|
|
10
|
+
def requires_webhookdb_auth? = false
|
|
11
|
+
def supports_webhooks? = false
|
|
12
|
+
|
|
13
|
+
def authorization_url(state:)
|
|
14
|
+
return "https://app.intercom.com/oauth?client_id=#{Webhookdb::Intercom.client_id}&state=#{state}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def exchange_authorization_code(code:)
|
|
18
|
+
token_resp = Webhookdb::Http.post(
|
|
19
|
+
"https://api.intercom.io/auth/eagle/token",
|
|
20
|
+
{
|
|
21
|
+
"client_id" => Webhookdb::Intercom.client_id,
|
|
22
|
+
"client_secret" => Webhookdb::Intercom.client_secret,
|
|
23
|
+
"code" => code,
|
|
24
|
+
},
|
|
25
|
+
logger: self.logger,
|
|
26
|
+
timeout: Webhookdb::Intercom.http_timeout,
|
|
27
|
+
)
|
|
28
|
+
return Webhookdb::Oauth::Tokens.new(access_token: token_resp.parsed_response["token"])
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def find_or_create_customer(tokens:, scope:)
|
|
32
|
+
intercom_user_resp = Webhookdb::Http.get(
|
|
33
|
+
"https://api.intercom.io/me",
|
|
34
|
+
headers: Webhookdb::Intercom.auth_headers(tokens.access_token),
|
|
35
|
+
logger: self.logger,
|
|
36
|
+
timeout: Webhookdb::Intercom.http_timeout,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
intercom_user = intercom_user_resp.parsed_response
|
|
40
|
+
scope[:me_response] = intercom_user
|
|
41
|
+
intercom_email = intercom_user.fetch("email")
|
|
42
|
+
return Webhookdb::Customer.find_or_create_for_email(intercom_email)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def build_marketplace_integrations(organization:, tokens:, scope:)
|
|
46
|
+
intercom_user = scope.fetch(:me_response)
|
|
47
|
+
# The intercom workspace id is used in the intercom webhook endpoint to identify which
|
|
48
|
+
# service integration to delegate requests to.
|
|
49
|
+
intercom_workspace_id = intercom_user.dig("app", "id_code")
|
|
50
|
+
root_sint = Webhookdb::ServiceIntegration.create_disambiguated(
|
|
51
|
+
"intercom_marketplace_root_v1",
|
|
52
|
+
organization:,
|
|
53
|
+
api_url: intercom_workspace_id,
|
|
54
|
+
backfill_key: tokens.access_token,
|
|
55
|
+
)
|
|
56
|
+
root_sint.replicator.build_dependents
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb/postgres"
|
|
4
|
+
require "webhookdb/oauth"
|
|
5
|
+
|
|
6
|
+
class Webhookdb::Oauth::Session < Webhookdb::Postgres::Model(:oauth_sessions)
|
|
7
|
+
plugin :timestamps
|
|
8
|
+
|
|
9
|
+
many_to_one :customer, class: "Webhookdb::Customer"
|
|
10
|
+
many_to_one :organization, class: "Webhookdb::Organization"
|
|
11
|
+
|
|
12
|
+
dataset_module do
|
|
13
|
+
def usable
|
|
14
|
+
return self.where(used_at: nil).where { created_at > 30.minutes.ago }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.params_for_request(request)
|
|
19
|
+
return {
|
|
20
|
+
peer_ip: request.ip,
|
|
21
|
+
user_agent: request.user_agent || "(unset)",
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Webhookdb::Oauth
|
|
4
|
+
class Tokens < Webhookdb::TypedStruct
|
|
5
|
+
attr_reader :access_token, :refresh_token
|
|
6
|
+
|
|
7
|
+
def initialize(**kwargs)
|
|
8
|
+
super
|
|
9
|
+
self.typecheck!(:access_token, String)
|
|
10
|
+
self.typecheck!(:refresh_token, String, nullable: true)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
|
15
|
+
class Provider
|
|
16
|
+
# @return [String] Unique key to identify the provider.
|
|
17
|
+
def key = raise NotImplementedError
|
|
18
|
+
|
|
19
|
+
# @return [String] Name of the app to present to users.
|
|
20
|
+
def app_name = raise NotImplementedError
|
|
21
|
+
|
|
22
|
+
# True if auth with this provider requires the user auth in WebhookDB,
|
|
23
|
+
# false if we can get their email from the Oauth process.
|
|
24
|
+
# If the access token can be used to get the 'me' user,
|
|
25
|
+
# we can usually use their email for the customer,
|
|
26
|
+
# but this may not be possible for some integrations.
|
|
27
|
+
def requires_webhookdb_auth? = raise NotImplementedError
|
|
28
|
+
|
|
29
|
+
# This is similar to `supports_webhooks` in the Replicator descriptors,
|
|
30
|
+
# except that this is used to make the success page dynamic.
|
|
31
|
+
# True if this provider's integrations support webhooks
|
|
32
|
+
# (real-time or user-built webhook payloads).
|
|
33
|
+
def supports_webhooks? = raise NotImplementedError
|
|
34
|
+
|
|
35
|
+
# @return [String] The Oauth URL to send users to to begin OAuth.
|
|
36
|
+
def authorization_url(state:) = raise NotImplementedError
|
|
37
|
+
|
|
38
|
+
# Exchange the access code (from the authorization url) for access and/or refresh tokens.
|
|
39
|
+
# @return [Webhookdb::Oauth::Tokens]
|
|
40
|
+
def exchange_authorization_code(code:) = raise NotImplementedError
|
|
41
|
+
|
|
42
|
+
# @param tokens [Webhookdb::Oauth::Tokens]
|
|
43
|
+
# @param scope [Hash] Used to store data needed in later calls, like when building integrations.
|
|
44
|
+
# @return [Array{TrueClass, FalseClass, Webhookdb::Customer}]
|
|
45
|
+
def find_or_create_customer(tokens:, scope:)
|
|
46
|
+
raise RuntimeError("should not be called") if self.requires_webhookdb_auth?
|
|
47
|
+
raise NotImplementedError
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Create the actual service integrations for the given org.
|
|
51
|
+
# @param organization [Webhookdb::Organization]
|
|
52
|
+
# @param tokens [Webhookdb::Oauth::Tokens]
|
|
53
|
+
# # @param scope [Hash]
|
|
54
|
+
def build_marketplace_integrations(organization:, tokens:, scope:) = raise NotImplementedError
|
|
55
|
+
end
|
|
56
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
|
57
|
+
|
|
58
|
+
class << self
|
|
59
|
+
# @return [String, Class]
|
|
60
|
+
def register(key, cls)
|
|
61
|
+
raise "#{key} already registered to #{cls}" if self.registry.include?(key)
|
|
62
|
+
self.registry[key] = cls
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @return [Provider]
|
|
66
|
+
def provider(key)
|
|
67
|
+
return self.registry.fetch(key).new
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @return [Hash]
|
|
71
|
+
def registry
|
|
72
|
+
return @registry ||= {}
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
require "webhookdb/oauth/front"
|
|
78
|
+
Webhookdb::Oauth.register(Webhookdb::Oauth::Front.new.key, Webhookdb::Oauth::Front)
|
|
79
|
+
require "webhookdb/oauth/intercom"
|
|
80
|
+
Webhookdb::Oauth.register(Webhookdb::Oauth::Intercom.new.key, Webhookdb::Oauth::Intercom)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "appydays/configurable"
|
|
4
|
+
require "appydays/loggable"
|
|
5
|
+
|
|
6
|
+
class Webhookdb::Organization::Alerting
|
|
7
|
+
include Appydays::Configurable
|
|
8
|
+
|
|
9
|
+
configurable(:alerting) do
|
|
10
|
+
setting :interval, 24.hours.to_i
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
attr_reader :org
|
|
14
|
+
|
|
15
|
+
def initialize(org)
|
|
16
|
+
@org = org
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Dispatch the message template to administrators of the org.
|
|
20
|
+
# @param message_template [Webhookdb::Message::Template]
|
|
21
|
+
def dispatch_alert(message_template)
|
|
22
|
+
unless message_template.respond_to?(:signature)
|
|
23
|
+
raise Webhookdb::InvalidPrecondition,
|
|
24
|
+
"message template #{message_template.template_name} must define a #signature method, " \
|
|
25
|
+
"which is a unique identity for this error type, used for grouping and idempotency"
|
|
26
|
+
end
|
|
27
|
+
signature = message_template.signature
|
|
28
|
+
self.org.admin_customers.each do |c|
|
|
29
|
+
idemkey = "orgalert-#{signature}-#{c.id}"
|
|
30
|
+
Webhookdb::Idempotency.every(Webhookdb::Organization::Alerting.interval).under_key(idemkey) do
|
|
31
|
+
message_template.dispatch_email(c)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Webhookdb::Organization::DatabaseMigration < Webhookdb::Postgres::Model(:organization_database_migrations)
|
|
4
|
+
include Webhookdb::Dbutil
|
|
5
|
+
|
|
6
|
+
class MigrationInProgress < Webhookdb::DatabaseLocked; end
|
|
7
|
+
class MigrationAlreadyFinished < StandardError; end
|
|
8
|
+
|
|
9
|
+
plugin :timestamps
|
|
10
|
+
plugin :column_encryption do |enc|
|
|
11
|
+
enc.column :source_admin_connection_url
|
|
12
|
+
enc.column :destination_admin_connection_url
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
many_to_one :started_by, class: "Webhookdb::Customer"
|
|
16
|
+
many_to_one :organization, class: "Webhookdb::Organization"
|
|
17
|
+
|
|
18
|
+
dataset_module do
|
|
19
|
+
def ongoing
|
|
20
|
+
return self.where(finished_at: nil)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.guard_ongoing!(org)
|
|
25
|
+
dbm = self.where(organization: org).ongoing.first
|
|
26
|
+
return if dbm.nil?
|
|
27
|
+
raise MigrationInProgress, "Organization #{org.name} has an ongoing database host migration so " \
|
|
28
|
+
"cannot be modified. We'll let admins know when it's done. Try again soon."
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.enqueue(admin_connection_url_raw:, readonly_connection_url_raw:, public_host:, started_by:, organization:)
|
|
32
|
+
self.guard_ongoing!(organization)
|
|
33
|
+
self.db.transaction do
|
|
34
|
+
dbm = self.create(
|
|
35
|
+
started_by:,
|
|
36
|
+
organization:,
|
|
37
|
+
organization_schema: organization.replication_schema,
|
|
38
|
+
source_admin_connection_url: organization.admin_connection_url_raw,
|
|
39
|
+
destination_admin_connection_url: admin_connection_url_raw,
|
|
40
|
+
)
|
|
41
|
+
organization.update(
|
|
42
|
+
public_host:,
|
|
43
|
+
admin_connection_url_raw:,
|
|
44
|
+
readonly_connection_url_raw:,
|
|
45
|
+
)
|
|
46
|
+
return dbm
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def displaysafe_source_url
|
|
51
|
+
return displaysafe_url(self.source_admin_connection_url)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def displaysafe_destination_url
|
|
55
|
+
return displaysafe_url(self.destination_admin_connection_url)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def status
|
|
59
|
+
return "finished" if self.finished_at.present?
|
|
60
|
+
return "in_progress" if self.started_at.present?
|
|
61
|
+
return "enqueued"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def finished?
|
|
65
|
+
return !!self.finished_at
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def migrate
|
|
69
|
+
raise MigrationAlreadyFinished if self.finished?
|
|
70
|
+
self.update(started_at: Time.now) if self.started_at.nil?
|
|
71
|
+
borrow_conn(self.source_admin_connection_url) do |srcdb|
|
|
72
|
+
borrow_conn(self.destination_admin_connection_url) do |dstdb|
|
|
73
|
+
self.organization.service_integrations.sort_by(&:id).each do |sint|
|
|
74
|
+
next if sint.id <= self.last_migrated_service_integration_id
|
|
75
|
+
self.migrate_service_integration(sint, srcdb, dstdb)
|
|
76
|
+
self.update(last_migrated_service_integration_id: sint.id, last_migrated_timestamp: nil)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
self.update(finished_at: Time.now)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @param [Webhookdb::ServiceIntegration] service_integration
|
|
84
|
+
protected def migrate_service_integration(service_integration, srcdb, dstdb)
|
|
85
|
+
svc = service_integration.replicator
|
|
86
|
+
# If the service integration was not synced in the old db, skip it
|
|
87
|
+
return unless srcdb.table_exists?(svc.qualified_table_sequel_identifier)
|
|
88
|
+
svc.create_table_modification(if_not_exists: true).execute(dstdb)
|
|
89
|
+
ds = srcdb[svc.qualified_table_sequel_identifier].order(svc.timestamp_column.name)
|
|
90
|
+
(ds = ds.where(Sequel[svc.timestamp_column.name] > self.last_migrated_timestamp)) unless
|
|
91
|
+
self.last_migrated_timestamp.nil?
|
|
92
|
+
chunksize = Webhookdb::Organization.database_migration_page_size
|
|
93
|
+
chunk = []
|
|
94
|
+
ds.paged_each(rows_per_fetch: chunksize, hold: true, cursor_name: "whdb_dbmigration_#{self.id}") do |row|
|
|
95
|
+
chunk << row
|
|
96
|
+
if chunk.size >= chunksize
|
|
97
|
+
self.upsert_chunk(service_integration, dstdb, chunk)
|
|
98
|
+
chunk.clear
|
|
99
|
+
Amigo::DurableJob.heartbeat
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
self.upsert_chunk(service_integration, dstdb, chunk)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# @param [Webhookdb::ServiceIntegration] service_integration
|
|
106
|
+
protected def upsert_chunk(service_integration, dstdb, chunk)
|
|
107
|
+
return if chunk.empty?
|
|
108
|
+
svc = service_integration.replicator
|
|
109
|
+
chunk.each { |h| h.delete(svc.primary_key_column.name) }
|
|
110
|
+
tscol = svc.timestamp_column.name
|
|
111
|
+
dstdb[svc.qualified_table_sequel_identifier].
|
|
112
|
+
insert_conflict(
|
|
113
|
+
target: svc.remote_key_column.name,
|
|
114
|
+
update_where: svc._update_where_expr,
|
|
115
|
+
).multi_insert(chunk)
|
|
116
|
+
self.update(last_migrated_timestamp: chunk.last[tscol])
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def finish(now: Time.now)
|
|
120
|
+
self.update(
|
|
121
|
+
finished_at: now,
|
|
122
|
+
source_admin_connection_url: displaysafe_source_url,
|
|
123
|
+
destination_admin_connection_url: displaysafe_destination_url,
|
|
124
|
+
)
|
|
125
|
+
return self
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Table: organization_database_migrations
|
|
130
|
+
# -------------------------------------------------------------------------------------------------------------------------
|
|
131
|
+
# Columns:
|
|
132
|
+
# id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
|
|
133
|
+
# created_at | timestamp with time zone | NOT NULL DEFAULT now()
|
|
134
|
+
# updated_at | timestamp with time zone |
|
|
135
|
+
# started_at | timestamp with time zone |
|
|
136
|
+
# finished_at | timestamp with time zone |
|
|
137
|
+
# organization_id | integer | NOT NULL
|
|
138
|
+
# started_by_id | integer |
|
|
139
|
+
# source_admin_connection_url | text |
|
|
140
|
+
# destination_admin_connection_url | text |
|
|
141
|
+
# organization_schema | text |
|
|
142
|
+
# last_migrated_service_integration_id | integer | NOT NULL DEFAULT 0
|
|
143
|
+
# last_migrated_timestamp | timestamp with time zone |
|
|
144
|
+
# Indexes:
|
|
145
|
+
# organization_database_migrations_pkey | PRIMARY KEY btree (id)
|
|
146
|
+
# one_inprogress_migration_per_org | UNIQUE btree (organization_id) WHERE finished_at IS NULL
|
|
147
|
+
# organization_database_migrations_organization_id_index | btree (organization_id)
|
|
148
|
+
# Foreign key constraints:
|
|
149
|
+
# organization_database_migrations_organization_id_fkey | (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
|
|
150
|
+
# organization_database_migrations_started_by_id_fkey | (started_by_id) REFERENCES customers(id) ON DELETE SET NULL
|
|
151
|
+
# -------------------------------------------------------------------------------------------------------------------------
|