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,429 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "appydays/configurable"
|
|
4
|
+
require "appydays/loggable"
|
|
5
|
+
|
|
6
|
+
require "webhookdb/organization"
|
|
7
|
+
require "webhookdb/cloudflare"
|
|
8
|
+
|
|
9
|
+
# When an org is created, it gets its own database containing the service integration tables
|
|
10
|
+
# exclusively for that org. This ensures orgs can be easily isolated from each other.
|
|
11
|
+
#
|
|
12
|
+
# Each org has two connections:
|
|
13
|
+
# - admin, which can modify tables. Used to create service integration tables.
|
|
14
|
+
# We generally want to avoid using this connection.
|
|
15
|
+
# Should never be exposed.
|
|
16
|
+
# - readonly, used for reading only the service integration tables
|
|
17
|
+
# (and not additional PG info tables).
|
|
18
|
+
# Can safely be exposed to members of the org.
|
|
19
|
+
class Webhookdb::Organization::DbBuilder
|
|
20
|
+
include Appydays::Configurable
|
|
21
|
+
include Appydays::Loggable
|
|
22
|
+
include Webhookdb::Dbutil
|
|
23
|
+
extend Webhookdb::MethodUtilities
|
|
24
|
+
|
|
25
|
+
class IsolatedOperationError < StandardError; end
|
|
26
|
+
|
|
27
|
+
DATABASE = "database"
|
|
28
|
+
SCHEMA = "schema"
|
|
29
|
+
USER = "user"
|
|
30
|
+
NONE = "none"
|
|
31
|
+
|
|
32
|
+
VALID_ISOLATION_MODES = [
|
|
33
|
+
"#{DATABASE}+#{USER}",
|
|
34
|
+
"#{DATABASE}+#{SCHEMA}+#{USER}",
|
|
35
|
+
SCHEMA,
|
|
36
|
+
"#{SCHEMA}+#{USER}",
|
|
37
|
+
NONE,
|
|
38
|
+
].freeze
|
|
39
|
+
|
|
40
|
+
singleton_attr_accessor :available_server_urls
|
|
41
|
+
|
|
42
|
+
configurable(:db_builder) do
|
|
43
|
+
# Server urls are absolute urls that define servers which can be chosen for organization DBs.
|
|
44
|
+
# Space-separate multiple servers.
|
|
45
|
+
setting :server_urls, [], convert: ->(s) { s.split.map(&:strip) }
|
|
46
|
+
# Server env vars are the names of environment variables whose value are
|
|
47
|
+
# each a server which can be chosen for organization DBs.
|
|
48
|
+
# Allows you to use dynamically configured servers.
|
|
49
|
+
# Space-separate multiple env vars.
|
|
50
|
+
setting :server_env_vars, ["DATABASE_URL"], convert: ->(s) { s.split.map(&:strip) }
|
|
51
|
+
# Determines whether we allow orgs to handle their own migrations.
|
|
52
|
+
# Used for self-hosted databases.
|
|
53
|
+
setting :allow_public_migrations, false
|
|
54
|
+
# Create a CNAME record when building the database connection.
|
|
55
|
+
setting :create_cname_for_connection_urls, false
|
|
56
|
+
# The Cloudflare zone ID that DNS records will be created in.
|
|
57
|
+
# NOTE: It is required that the Cloudflare API token has access to this zone.
|
|
58
|
+
setting :cloudflare_dns_zone_id, "testdnszoneid"
|
|
59
|
+
# See README for more details.
|
|
60
|
+
setting :isolation_mode, "database+user"
|
|
61
|
+
# How many connections can each readonly user make?
|
|
62
|
+
setting :readonly_connection_limit, 50
|
|
63
|
+
# How long can the readonly user query before a timeout?
|
|
64
|
+
# Keep this low to avoid query pressure the database,
|
|
65
|
+
# especially when dealing with many tenants.
|
|
66
|
+
setting :readonly_connection_timeout, "15s"
|
|
67
|
+
|
|
68
|
+
after_configured do
|
|
69
|
+
unless VALID_ISOLATION_MODES.include?(self.isolation_mode)
|
|
70
|
+
msg = "Invalid DB_BUILDER_ISOLATION_MODE '#{self.isolation_mode}', " \
|
|
71
|
+
"valid modes are: #{VALID_ISOLATION_MODES.join(', ')}"
|
|
72
|
+
raise KeyError, msg
|
|
73
|
+
end
|
|
74
|
+
self.available_server_urls = self.server_urls.dup
|
|
75
|
+
self.available_server_urls.concat(self.server_env_vars.map { |e| ENV.fetch(e, nil) })
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.isolate?(type)
|
|
80
|
+
return self.isolation_mode.include?(type)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
attr_reader :admin_url, :readonly_url
|
|
84
|
+
|
|
85
|
+
def initialize(org)
|
|
86
|
+
@org = org
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def default_replication_schema
|
|
90
|
+
raise Webhookdb::InvalidPrecondition, "Org must have a key to calculate the replication schema" if @org.key.blank?
|
|
91
|
+
return "public" unless self.class.isolate?(SCHEMA)
|
|
92
|
+
return "whdb_#{@org.key}"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def prepare_database_connections
|
|
96
|
+
# Grab a random server url. This will give us a 'superuser'-like url
|
|
97
|
+
# that can create roles and whatever else.
|
|
98
|
+
superuser_str = self._choose_superuser_url
|
|
99
|
+
case self.class.isolation_mode
|
|
100
|
+
when "database+user"
|
|
101
|
+
self._prepare_database_connections_database_user(superuser_str)
|
|
102
|
+
when "database+schema+user"
|
|
103
|
+
self._prepare_database_connections_database_schema_user(superuser_str)
|
|
104
|
+
when "schema"
|
|
105
|
+
self._prepare_database_connections_schema(superuser_str)
|
|
106
|
+
when "schema+user"
|
|
107
|
+
self._prepare_database_connections_schema_user(superuser_str)
|
|
108
|
+
when "none"
|
|
109
|
+
self._prepare_database_connections_none(superuser_str)
|
|
110
|
+
else
|
|
111
|
+
raise "Did not expect mode #{self.class.isolation_mode}"
|
|
112
|
+
end
|
|
113
|
+
return self
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def _prepare_database_connections_database_user(superuser_url_str)
|
|
117
|
+
superuser_url = URI.parse(superuser_url_str)
|
|
118
|
+
# Use this superuser connection to create the admin role,
|
|
119
|
+
# which will be responsible for the database.
|
|
120
|
+
# While connected as the superuser, we can go ahead and create both roles.
|
|
121
|
+
admin_user = self.randident("ad")
|
|
122
|
+
admin_pwd = self.randident
|
|
123
|
+
ro_user = self.randident("ro")
|
|
124
|
+
ro_pwd = self.randident
|
|
125
|
+
dbname = self.randident("db")
|
|
126
|
+
# Do not log this
|
|
127
|
+
borrow_conn(superuser_url_str, loggers: []) do |conn|
|
|
128
|
+
conn << <<~SQL
|
|
129
|
+
CREATE ROLE #{admin_user} PASSWORD '#{admin_pwd}' NOSUPERUSER CREATEDB CREATEROLE INHERIT LOGIN;
|
|
130
|
+
CREATE ROLE #{ro_user} PASSWORD '#{ro_pwd}' NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT LOGIN;
|
|
131
|
+
ALTER USER #{ro_user} WITH CONNECTION LIMIT #{self.class.readonly_connection_limit};
|
|
132
|
+
ALTER ROLE #{ro_user} SET statement_timeout = '#{self.class.readonly_connection_timeout}';
|
|
133
|
+
GRANT #{admin_user} TO CURRENT_USER;
|
|
134
|
+
SQL
|
|
135
|
+
# Cannot be in the same statement as above since that's one transaction.
|
|
136
|
+
conn << "CREATE DATABASE #{dbname} OWNER #{admin_user};"
|
|
137
|
+
conn << "REVOKE ALL PRIVILEGES ON DATABASE #{dbname} FROM public;"
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Now that we've created the admin role and have a database,
|
|
141
|
+
# we must disconnect and connect to the new database.
|
|
142
|
+
# This MUST be done as superuser; for some reason,
|
|
143
|
+
# the public schema (which we need to revoke on) belongs to the superuser,
|
|
144
|
+
# NOT the DB owner: https://pgsql-general.postgresql.narkive.com/X9VKOPIW
|
|
145
|
+
superuser_in_db_str = self._create_conn_url(superuser_url.user, superuser_url.password, superuser_url, dbname)
|
|
146
|
+
schema = self._org_schema
|
|
147
|
+
borrow_conn(superuser_in_db_str) do |conn|
|
|
148
|
+
conn << <<~SQL
|
|
149
|
+
-- Revoke all rights from readonly user, and public role, which all users have.
|
|
150
|
+
REVOKE ALL ON DATABASE #{dbname} FROM PUBLIC, #{ro_user};
|
|
151
|
+
REVOKE ALL ON SCHEMA public FROM PUBLIC, #{ro_user};
|
|
152
|
+
REVOKE ALL ON ALL TABLES IN SCHEMA public FROM PUBLIC, #{ro_user};
|
|
153
|
+
-- Create the schema if needed. In most cases this is 'public' so it isn't.
|
|
154
|
+
CREATE SCHEMA IF NOT EXISTS #{schema};
|
|
155
|
+
-- Allow the readonly user to select stuff
|
|
156
|
+
GRANT CONNECT ON DATABASE #{dbname} TO #{ro_user};
|
|
157
|
+
GRANT USAGE ON SCHEMA #{schema} TO #{ro_user};
|
|
158
|
+
GRANT SELECT ON ALL TABLES IN SCHEMA #{schema} TO #{ro_user};
|
|
159
|
+
-- Now that we have modified public/replication schema as superuser,
|
|
160
|
+
-- we can grant ownership to the admin user, so they can do modification in the future.
|
|
161
|
+
ALTER SCHEMA public OWNER TO #{admin_user};
|
|
162
|
+
ALTER SCHEMA #{schema} OWNER TO #{admin_user};
|
|
163
|
+
SQL
|
|
164
|
+
end
|
|
165
|
+
@admin_url = self._create_conn_url(admin_user, admin_pwd, superuser_url, dbname)
|
|
166
|
+
# We MUST modify the default privs AFTER changing ownership.
|
|
167
|
+
# Changing ownership seems to reset default piv grants (and it cannot be done after transferring ownership)
|
|
168
|
+
borrow_conn(@admin_url) do |conn|
|
|
169
|
+
conn << "ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema} GRANT SELECT ON TABLES TO #{ro_user};"
|
|
170
|
+
end
|
|
171
|
+
@readonly_url = self._create_conn_url(ro_user, ro_pwd, superuser_url, dbname)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def _prepare_database_connections_database_schema_user(superuser_url_str)
|
|
175
|
+
self._prepare_database_connections_database_user(superuser_url_str)
|
|
176
|
+
# Revoke everything on public schema, so our readonly user cannot access it.
|
|
177
|
+
borrow_conn(@admin_url) do |conn|
|
|
178
|
+
conn << "REVOKE ALL ON SCHEMA public FROM public"
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def _prepare_database_connections_schema(superuser_url_str)
|
|
183
|
+
borrow_conn(superuser_url_str) do |conn|
|
|
184
|
+
conn << "CREATE SCHEMA IF NOT EXISTS #{self._org_schema};"
|
|
185
|
+
end
|
|
186
|
+
@admin_url = superuser_url_str
|
|
187
|
+
@readonly_url = superuser_url_str
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def _prepare_database_connections_schema_user(superuser_url_str)
|
|
191
|
+
ro_user = self.randident("ro")
|
|
192
|
+
ro_pwd = self.randident
|
|
193
|
+
schema = self._org_schema
|
|
194
|
+
borrow_conn(superuser_url_str) do |conn|
|
|
195
|
+
conn << <<~SQL
|
|
196
|
+
-- Create readonly role and make sure it cannot access public stuff
|
|
197
|
+
CREATE ROLE #{ro_user} PASSWORD '#{ro_pwd}' NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT LOGIN;
|
|
198
|
+
ALTER USER #{ro_user} WITH CONNECTION LIMIT #{self.class.readonly_connection_limit};
|
|
199
|
+
ALTER ROLE #{ro_user} SET statement_timeout = '#{self.class.readonly_connection_timeout}';
|
|
200
|
+
REVOKE ALL ON SCHEMA public FROM #{ro_user};
|
|
201
|
+
REVOKE CREATE ON SCHEMA public FROM #{ro_user};
|
|
202
|
+
REVOKE ALL ON ALL TABLES IN SCHEMA public FROM #{ro_user};
|
|
203
|
+
-- Create the schema and ensure readonly user can access it
|
|
204
|
+
-- Also remove public role access so other readonly users cannot access it.
|
|
205
|
+
CREATE SCHEMA IF NOT EXISTS #{schema};
|
|
206
|
+
REVOKE ALL ON SCHEMA #{schema} FROM PUBLIC, #{ro_user};
|
|
207
|
+
REVOKE ALL ON ALL TABLES IN SCHEMA #{schema} FROM PUBLIC, #{ro_user};
|
|
208
|
+
GRANT USAGE ON SCHEMA #{schema} TO #{ro_user};
|
|
209
|
+
GRANT SELECT ON ALL TABLES IN SCHEMA #{schema} TO #{ro_user};
|
|
210
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema} GRANT SELECT ON TABLES TO #{ro_user};
|
|
211
|
+
SQL
|
|
212
|
+
end
|
|
213
|
+
@admin_url = superuser_url_str
|
|
214
|
+
@readonly_url = self._replace_url_auth(superuser_url_str, ro_user, ro_pwd)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def _prepare_database_connections_none(superuser_url_str)
|
|
218
|
+
@admin_url = superuser_url_str
|
|
219
|
+
@readonly_url = superuser_url_str
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Return the superuser url for the org to use when creating its DB connections.
|
|
223
|
+
# Right now this is just choosing a random server url,
|
|
224
|
+
# but could be more complex, like choosing the server with the lowest utilization.
|
|
225
|
+
protected def _choose_superuser_url
|
|
226
|
+
superuser_str = self.class.available_server_urls.sample
|
|
227
|
+
return superuser_str
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
protected def _org_schema
|
|
231
|
+
return Webhookdb::DBAdapter::PG.new.escape_identifier(@org.replication_schema)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# prefix with <id>a to avoid ever conflicting database names
|
|
235
|
+
def randident(prefix="")
|
|
236
|
+
return "a#{prefix}#{@org.id}a#{SecureRandom.hex(8)}"
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def _create_conn_url(username, password, uri, dbname)
|
|
240
|
+
return "postgres://#{username}:#{password}@#{uri.host}:#{uri.port}/#{dbname}"
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def _replace_url_auth(url, user, pass)
|
|
244
|
+
uri = URI(url)
|
|
245
|
+
uri.user = user
|
|
246
|
+
uri.password = pass
|
|
247
|
+
return uri.to_s
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Create the CNAME record specific to this org,
|
|
251
|
+
# so that the DB it is hosted on is reachable via a nice URL,
|
|
252
|
+
# rather than connecting directly to the host.
|
|
253
|
+
#
|
|
254
|
+
# If create_cnames is false,
|
|
255
|
+
# this method no-ops, so we don't spam cloudflare during integration tests,
|
|
256
|
+
# or need to httpmock everything during unit tests.
|
|
257
|
+
#
|
|
258
|
+
# Otherwise, create the CNAME like "myorg.db node.rds.amazonaws.com",
|
|
259
|
+
# and set org.public_host to "myorg.db.webhookdb.dev". The "webhookdb.dev" value
|
|
260
|
+
# comes from the Cloudflare DNS response, and corresponds to the zone
|
|
261
|
+
# that `cloudflare_dns_zone_id` identifies.
|
|
262
|
+
def create_public_host_cname(conn_url)
|
|
263
|
+
return nil unless self.class.create_cname_for_connection_urls
|
|
264
|
+
db_host = URI.parse(conn_url).host
|
|
265
|
+
cname = Webhookdb::Cloudflare.create_zone_dns_record(
|
|
266
|
+
type: "CNAME",
|
|
267
|
+
zone_id: self.class.cloudflare_dns_zone_id,
|
|
268
|
+
name: "#{@org.key}.db",
|
|
269
|
+
content: db_host,
|
|
270
|
+
)
|
|
271
|
+
@org.public_host = cname["result"]["name"]
|
|
272
|
+
@org.cloudflare_dns_record_json = cname
|
|
273
|
+
return self
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# To remove related databases and users, we must
|
|
277
|
+
# 1) find the server hosting the database, which will itself contain the admin creds
|
|
278
|
+
# suitable for having created it in the first place.
|
|
279
|
+
# 2) delete the database, using info extracted from the admin connection (run from the server conn)
|
|
280
|
+
# 3) delete each user (run from the server conn)
|
|
281
|
+
# We need these workarounds because we cannot drop the admin database while we're connected to it
|
|
282
|
+
# (and we probably don't want the admin role trying to delete itself).
|
|
283
|
+
def remove_related_database
|
|
284
|
+
return if @org.admin_connection_url_raw.blank?
|
|
285
|
+
superuser_str = self._find_superuser_url_str
|
|
286
|
+
# Cannot use conn cache since we may be removing ourselves
|
|
287
|
+
borrow_conn(superuser_str) do |conn|
|
|
288
|
+
case self.class.isolation_mode
|
|
289
|
+
when "database+user", "database+schema+user"
|
|
290
|
+
Webhookdb::ConnectionCache.disconnect(@org.admin_connection_url_raw)
|
|
291
|
+
Webhookdb::ConnectionCache.disconnect(@org.readonly_connection_url_raw)
|
|
292
|
+
conn << "DROP DATABASE #{@org.dbname};"
|
|
293
|
+
conn << "DROP USER #{@org.readonly_user};" unless @org.single_db_user?
|
|
294
|
+
conn << "DROP USER #{@org.admin_user};"
|
|
295
|
+
when "schema+user"
|
|
296
|
+
Webhookdb::ConnectionCache.disconnect(@org.readonly_connection_url_raw)
|
|
297
|
+
conn << <<~SQL
|
|
298
|
+
DROP SCHEMA IF EXISTS #{self._org_schema} CASCADE;
|
|
299
|
+
DROP OWNED BY #{@org.readonly_user};
|
|
300
|
+
DROP USER #{@org.readonly_user};
|
|
301
|
+
SQL
|
|
302
|
+
when "schema"
|
|
303
|
+
conn << "DROP SCHEMA IF EXISTS #{self._org_schema} CASCADE"
|
|
304
|
+
when "none"
|
|
305
|
+
nil
|
|
306
|
+
else
|
|
307
|
+
raise "not supported yet"
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
protected def _find_superuser_url_str
|
|
313
|
+
admin_url = URI.parse(@org.admin_connection_url_raw)
|
|
314
|
+
superuser_str = self.class.available_server_urls.find do |sstr|
|
|
315
|
+
surl = URI.parse(sstr)
|
|
316
|
+
surl.host == admin_url.host && surl.port == admin_url.port
|
|
317
|
+
end
|
|
318
|
+
if superuser_str.blank?
|
|
319
|
+
msg = "Could not find a matching server url for #{admin_url} in #{self.class.available_server_urls}"
|
|
320
|
+
raise msg
|
|
321
|
+
end
|
|
322
|
+
return superuser_str
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def roll_connection_credentials
|
|
326
|
+
raise IsolatedOperationError, "cannot roll credentials without a user isolation mode" unless
|
|
327
|
+
self.class.isolate?(USER)
|
|
328
|
+
superuser_uri = URI(self._find_superuser_url_str)
|
|
329
|
+
orig_readonly_user = URI(@org.readonly_connection_url_raw).user
|
|
330
|
+
ro_user = self.randident("ro")
|
|
331
|
+
ro_pwd = self.randident
|
|
332
|
+
@readonly_url = self._create_conn_url(ro_user, ro_pwd, superuser_uri, @org.dbname)
|
|
333
|
+
lines = [
|
|
334
|
+
"ALTER ROLE #{orig_readonly_user} RENAME TO #{ro_user};",
|
|
335
|
+
"ALTER ROLE #{ro_user} WITH PASSWORD '#{ro_pwd}';",
|
|
336
|
+
]
|
|
337
|
+
if self.class.isolate?(DATABASE)
|
|
338
|
+
# Roll admin credentials for a separate database.
|
|
339
|
+
# For schema isolation, we assume admin is the superuser so cannot roll creds.
|
|
340
|
+
orig_admin_user = URI(@org.admin_connection_url_raw).user
|
|
341
|
+
admin_user = self.randident("ad")
|
|
342
|
+
admin_pwd = self.randident
|
|
343
|
+
lines.push(
|
|
344
|
+
"ALTER ROLE #{orig_admin_user} RENAME TO #{admin_user};",
|
|
345
|
+
"ALTER ROLE #{admin_user} WITH PASSWORD '#{admin_pwd}';",
|
|
346
|
+
)
|
|
347
|
+
@admin_url = self._create_conn_url(admin_user, admin_pwd, superuser_uri, @org.dbname)
|
|
348
|
+
else
|
|
349
|
+
@admin_url = @org.admin_connection_url_raw
|
|
350
|
+
end
|
|
351
|
+
# New conn so we don't log it
|
|
352
|
+
borrow_conn(superuser_uri.to_s, loggers: []) do |conn|
|
|
353
|
+
conn << lines.join("\n")
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def generate_fdw_payload(
|
|
358
|
+
remote_server_name:,
|
|
359
|
+
fetch_size:,
|
|
360
|
+
local_schema:,
|
|
361
|
+
view_schema:
|
|
362
|
+
)
|
|
363
|
+
raise ArgumentError, "no arg can be blank" if
|
|
364
|
+
[remote_server_name, fetch_size, local_schema, view_schema].any?(&:blank?)
|
|
365
|
+
conn = URI(@org.readonly_connection_url)
|
|
366
|
+
fdw_sql = <<~FDW_SERVER
|
|
367
|
+
CREATE EXTENSION IF NOT EXISTS postgres_fdw;
|
|
368
|
+
DROP SERVER IF EXISTS #{remote_server_name} CASCADE;
|
|
369
|
+
CREATE SERVER #{remote_server_name}
|
|
370
|
+
FOREIGN DATA WRAPPER postgres_fdw
|
|
371
|
+
OPTIONS (host '#{conn.host}', port '#{conn.port}', dbname '#{conn.path[1..]}', fetch_size '#{fetch_size}');
|
|
372
|
+
|
|
373
|
+
CREATE USER MAPPING FOR CURRENT_USER
|
|
374
|
+
SERVER #{remote_server_name}
|
|
375
|
+
OPTIONS (user '#{conn.user}', password '#{conn.password}');
|
|
376
|
+
|
|
377
|
+
CREATE SCHEMA IF NOT EXISTS #{local_schema};
|
|
378
|
+
IMPORT FOREIGN SCHEMA #{self._org_schema}
|
|
379
|
+
FROM SERVER #{remote_server_name}
|
|
380
|
+
INTO #{local_schema};
|
|
381
|
+
|
|
382
|
+
CREATE SCHEMA IF NOT EXISTS #{view_schema};
|
|
383
|
+
FDW_SERVER
|
|
384
|
+
|
|
385
|
+
views_for_integrations = @org.service_integrations.to_h do |sint|
|
|
386
|
+
cmd = "CREATE MATERIALIZED VIEW IF NOT EXISTS #{view_schema}.#{sint.service_name} " \
|
|
387
|
+
"AS SELECT * FROM #{local_schema}.#{sint.table_name};"
|
|
388
|
+
[sint.opaque_id, cmd]
|
|
389
|
+
end
|
|
390
|
+
views_sql = views_for_integrations.values.sort.join("\n")
|
|
391
|
+
|
|
392
|
+
result = {
|
|
393
|
+
fdw_sql:,
|
|
394
|
+
views_sql:,
|
|
395
|
+
compound_sql: "#{fdw_sql}\n\n#{views_sql}",
|
|
396
|
+
views: views_for_integrations,
|
|
397
|
+
}
|
|
398
|
+
return result
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def migration_replication_schema_sql(old_schema, new_schema)
|
|
402
|
+
can_migrate_to_public = self.class.isolate?(DATABASE)
|
|
403
|
+
if new_schema == "public" && !can_migrate_to_public
|
|
404
|
+
raise IsolatedOperationError,
|
|
405
|
+
"cannot migrate to public schema when using '#{self.class.isolation_mode}' isolation"
|
|
406
|
+
end
|
|
407
|
+
ad = Webhookdb::DBAdapter::PG.new
|
|
408
|
+
qold_schema = ad.escape_identifier(old_schema)
|
|
409
|
+
qnew_schema = ad.escape_identifier(new_schema)
|
|
410
|
+
lines = []
|
|
411
|
+
# lines << "ALTER SCHEMA #{qold_schema} RENAME TO #{qnew_schema};"
|
|
412
|
+
# lines << "CREATE SCHEMA IF NOT EXISTS public;"
|
|
413
|
+
lines << "CREATE SCHEMA IF NOT EXISTS #{qnew_schema};"
|
|
414
|
+
@org.service_integrations.each do |sint|
|
|
415
|
+
lines << ("ALTER TABLE IF EXISTS %s.%s SET SCHEMA %s;" %
|
|
416
|
+
[qold_schema, ad.escape_identifier(sint.table_name), qnew_schema])
|
|
417
|
+
end
|
|
418
|
+
if self.class.isolate?(USER)
|
|
419
|
+
ro_user = @org.readonly_user
|
|
420
|
+
lines << "GRANT USAGE ON SCHEMA #{qnew_schema} TO #{ro_user};"
|
|
421
|
+
lines << "GRANT SELECT ON ALL TABLES IN SCHEMA #{qnew_schema} TO #{ro_user};"
|
|
422
|
+
lines << "REVOKE ALL ON SCHEMA #{qold_schema} FROM #{ro_user};" unless @org.single_db_user?
|
|
423
|
+
lines << "REVOKE ALL ON ALL TABLES IN SCHEMA #{qold_schema} FROM #{ro_user};" unless @org.single_db_user?
|
|
424
|
+
lines << "ALTER DEFAULT PRIVILEGES IN SCHEMA #{qnew_schema} GRANT SELECT ON TABLES TO #{ro_user};"
|
|
425
|
+
end
|
|
426
|
+
# lines << "DROP SCHEMA #{qold_schema} CASCADE;"
|
|
427
|
+
return lines.join("\n")
|
|
428
|
+
end
|
|
429
|
+
end
|