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,232 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb/api"
|
|
4
|
+
require "webhookdb/jobs/sync_target_run_sync"
|
|
5
|
+
|
|
6
|
+
# rubocop:disable Layout/LineLength
|
|
7
|
+
class Webhookdb::API::SyncTargets < Webhookdb::API::V1
|
|
8
|
+
class ConnectionUrlType < Grape::Validations::Validators::Base
|
|
9
|
+
def validate!(params)
|
|
10
|
+
url = params[:connection_url]
|
|
11
|
+
case @option
|
|
12
|
+
when "db", "http"
|
|
13
|
+
if (err = Webhookdb::SyncTarget.send(:"validate_#{@option}_url", url))
|
|
14
|
+
raise Grape::Exceptions::Validation.new params: [url], message: err
|
|
15
|
+
end
|
|
16
|
+
else
|
|
17
|
+
raise Grape::Exceptions::Validation.new params: [url], message: "#{@option} is not a valid connection url type"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
resource :organizations do
|
|
23
|
+
route_param :org_identifier, type: String do
|
|
24
|
+
resource :sync_targets do
|
|
25
|
+
[:db, :http].each do |target_type_resource|
|
|
26
|
+
resource target_type_resource do
|
|
27
|
+
route_setting :target_type, {key: target_type_resource}
|
|
28
|
+
helpers do
|
|
29
|
+
is_db = target_type_resource == :db
|
|
30
|
+
def target_type
|
|
31
|
+
tgttype = route.settings[:target_type]
|
|
32
|
+
# Doesn't appear that this cascades, so we need to specify on every verb method.
|
|
33
|
+
raise "add `route_setting :target_type, target_type_resource` before verb" unless tgttype
|
|
34
|
+
return tgttype
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def db? = target_type == :db
|
|
38
|
+
|
|
39
|
+
default_period = Webhookdb::SyncTarget.default_valid_period
|
|
40
|
+
params :sync_target_params do
|
|
41
|
+
optional :period_seconds,
|
|
42
|
+
type: Integer,
|
|
43
|
+
values: Webhookdb::SyncTarget.valid_period(1),
|
|
44
|
+
prompt: {
|
|
45
|
+
message: "How many seconds between syncs (#{default_period.begin} to #{default_period.end}):",
|
|
46
|
+
disable: ->(req) { req.path.end_with?("/update") },
|
|
47
|
+
}
|
|
48
|
+
is_db && optional(:schema,
|
|
49
|
+
type: String,
|
|
50
|
+
db_identifier: true,
|
|
51
|
+
allow_blank: true,
|
|
52
|
+
desc: "Schema (or namespace) to write the table into. Default to no schema/namespace.",)
|
|
53
|
+
# The description here says there is a default value, but the default value isn't actually saved to the SyncTarget
|
|
54
|
+
# object--it's inferred in the SyncTarget sync behavior when the table value is a blank string.
|
|
55
|
+
is_db && optional(:table,
|
|
56
|
+
type: String,
|
|
57
|
+
db_identifier: true,
|
|
58
|
+
allow_blank: true,
|
|
59
|
+
desc: "Table to create and update. Default to match the table name of the service integration.",)
|
|
60
|
+
!is_db && optional(:page_size,
|
|
61
|
+
type: Integer,
|
|
62
|
+
values: (1..1000),
|
|
63
|
+
desc: "Maximum number of rows in each POST request.",)
|
|
64
|
+
end
|
|
65
|
+
params :connection_url do
|
|
66
|
+
optional :connection_url,
|
|
67
|
+
type: String,
|
|
68
|
+
prompt: "Enter the #{is_db ? 'database connection string' : 'HTTP endpoint'} that WebhookDB should sync data to:",
|
|
69
|
+
connection_url_type: is_db ? "db" : "http"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def entity
|
|
73
|
+
return db? ? Webhookdb::API::DbSyncTargetEntity : Webhookdb::API::HttpSyncTargetEntity
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def validate_period!(org, value)
|
|
77
|
+
r = Webhookdb::SyncTarget.valid_period_for(org)
|
|
78
|
+
return if r.cover?(value)
|
|
79
|
+
err = "The valid sync period for organization #{org.name} is between #{r.begin} and #{r.end} seconds."
|
|
80
|
+
invalid!(err, message: err)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def verify_connection(url)
|
|
84
|
+
if db?
|
|
85
|
+
Webhookdb::SyncTarget.verify_db_connection(url)
|
|
86
|
+
else
|
|
87
|
+
Webhookdb::SyncTarget.verify_http_connection(url)
|
|
88
|
+
end
|
|
89
|
+
rescue Webhookdb::SyncTarget::InvalidConnection => e
|
|
90
|
+
merror!(400, e.to_s, code: "invalid_sync_target_connection")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def predicate = db? ? :db? : :http?
|
|
94
|
+
def fullname = db? ? "database" : "http"
|
|
95
|
+
def cmd = db? ? "dbsync" : "httpsync"
|
|
96
|
+
def tgtname = db? ? "database" : "endpoint"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
route_setting :target_type, target_type_resource
|
|
100
|
+
get do
|
|
101
|
+
org = lookup_org!
|
|
102
|
+
subs = org.all_sync_targets.filter(&predicate)
|
|
103
|
+
message = ""
|
|
104
|
+
if subs.empty?
|
|
105
|
+
message = "Organization #{org.name} has no #{fullname} sync targets set up.\n" \
|
|
106
|
+
"Use `webhookdb #{cmd} create` to synchronization WebhookDB tables to your #{tgtname}."
|
|
107
|
+
end
|
|
108
|
+
status 200
|
|
109
|
+
present_collection subs, with: entity, message:
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
params do
|
|
113
|
+
use :connection_url
|
|
114
|
+
use :sync_target_params
|
|
115
|
+
optional :service_integration_opaque_id,
|
|
116
|
+
type: String, allow_blank: false,
|
|
117
|
+
desc: "This is a deprecated parameter. In the future, please use `service_integration_identifier`."
|
|
118
|
+
optional :service_integration_identifier, type: String, allow_blank: false
|
|
119
|
+
at_least_one_of :service_integration_opaque_id, :service_integration_identifier
|
|
120
|
+
end
|
|
121
|
+
route_setting :target_type, target_type_resource
|
|
122
|
+
post :create do
|
|
123
|
+
customer = current_customer
|
|
124
|
+
org = lookup_org!(customer:)
|
|
125
|
+
ensure_admin!(org, customer:)
|
|
126
|
+
identifier = params[:service_integration_identifier] || params[:service_integration_opaque_id]
|
|
127
|
+
sint = lookup_service_integration!(org, identifier)
|
|
128
|
+
|
|
129
|
+
validate_period!(sint.organization, params[:period_seconds])
|
|
130
|
+
verify_connection(params[:connection_url])
|
|
131
|
+
stgt = Webhookdb::SyncTarget.create(
|
|
132
|
+
service_integration: sint,
|
|
133
|
+
connection_url: params[:connection_url],
|
|
134
|
+
period_seconds: params[:period_seconds],
|
|
135
|
+
schema: params[:schema] || "",
|
|
136
|
+
table: params[:table] || "",
|
|
137
|
+
page_size: params[:page_size],
|
|
138
|
+
created_by: customer,
|
|
139
|
+
)
|
|
140
|
+
message = "Every #{stgt.period_seconds} seconds, data from #{sint.service_name} " \
|
|
141
|
+
"in #{sint.table_name} will be synchronized to #{stgt.displaysafe_connection_url}"
|
|
142
|
+
status 200
|
|
143
|
+
present stgt, with: entity, message:
|
|
144
|
+
rescue Grape::Exceptions::Validation => e
|
|
145
|
+
merror!(400, e.message)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
route_param :opaque_id do
|
|
149
|
+
helpers do
|
|
150
|
+
def lookup!
|
|
151
|
+
org = lookup_org!
|
|
152
|
+
ensure_admin!(org)
|
|
153
|
+
(stgt = org.all_sync_targets_dataset[Sequel[:sync_targets][:opaque_id] => params[:opaque_id]])
|
|
154
|
+
merror!(403, "There is no #{fullname} sync target with that id.") if
|
|
155
|
+
stgt.nil? || !stgt.send(predicate)
|
|
156
|
+
return stgt
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
params do
|
|
160
|
+
optional :user, type: String, prompt: "Username for the connection:"
|
|
161
|
+
optional :password, type: String, prompt: "Password for the connection:"
|
|
162
|
+
end
|
|
163
|
+
route_setting :target_type, target_type_resource
|
|
164
|
+
post :update_credentials do
|
|
165
|
+
stgt = lookup!
|
|
166
|
+
uri = URI(stgt.connection_url)
|
|
167
|
+
uri.user = params[:user]
|
|
168
|
+
uri.password = params[:password]
|
|
169
|
+
verify_connection(uri.to_s)
|
|
170
|
+
stgt.update(connection_url: uri.to_s)
|
|
171
|
+
status 200
|
|
172
|
+
present stgt, with: entity, message: "Connection URL has been updated."
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
params do
|
|
176
|
+
use :sync_target_params
|
|
177
|
+
end
|
|
178
|
+
route_setting :target_type, target_type_resource
|
|
179
|
+
post :update do
|
|
180
|
+
stgt = lookup!
|
|
181
|
+
stgt.table = params[:table] if params.key?(:table)
|
|
182
|
+
stgt.schema = params[:schema] if params.key?(:schema)
|
|
183
|
+
stgt.page_size = params[:page_size] if params.key?(:page_size)
|
|
184
|
+
if params.key?(:period_seconds) && (period_seconds = params[:period_seconds])
|
|
185
|
+
validate_period!(stgt.organization, period_seconds)
|
|
186
|
+
stgt.period_seconds = period_seconds
|
|
187
|
+
end
|
|
188
|
+
save_or_error!(stgt)
|
|
189
|
+
status 200
|
|
190
|
+
present stgt, with: entity, message: "#{fullname.capitalize} sync target has been updated."
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
params do
|
|
194
|
+
optional :confirm, type: String
|
|
195
|
+
end
|
|
196
|
+
route_setting :target_type, target_type_resource
|
|
197
|
+
post :delete do
|
|
198
|
+
stgt = lookup!
|
|
199
|
+
prompted_table = stgt.service_integration.table_name
|
|
200
|
+
if prompted_table != params[:confirm]&.strip
|
|
201
|
+
Webhookdb::API::Helpers.prompt_for_required_param!(
|
|
202
|
+
request,
|
|
203
|
+
:confirm,
|
|
204
|
+
"Please confirm your deletion by entering the table name that is being synced by " \
|
|
205
|
+
"this #{fullname} sync target (#{prompted_table}):",
|
|
206
|
+
)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
stgt.logger.warn("destroying_sync_target", sync_target_id: stgt.id, customer_id: current_customer.id)
|
|
210
|
+
stgt.destroy
|
|
211
|
+
status 200
|
|
212
|
+
message = "#{fullname.capitalize} sync target has been removed and will no longer sync."
|
|
213
|
+
present stgt, with: entity, message:
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
route_setting :target_type, target_type_resource
|
|
217
|
+
post :sync do
|
|
218
|
+
stgt = lookup!
|
|
219
|
+
status 200
|
|
220
|
+
next_sync = stgt.next_possible_sync(now: Time.now)
|
|
221
|
+
Webhookdb::Jobs::SyncTargetRunSync.perform_at(next_sync, stgt.id)
|
|
222
|
+
message = "#{fullname.capitalize} sync has been scheduled. It should start at about #{next_sync}."
|
|
223
|
+
present stgt, with: entity, message:
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
# rubocop:enable Layout/LineLength
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "grape"
|
|
4
|
+
|
|
5
|
+
require "webhookdb/api"
|
|
6
|
+
require "webhookdb/postgres"
|
|
7
|
+
|
|
8
|
+
# Health check and other metadata endpoints.
|
|
9
|
+
class Webhookdb::API::System < Webhookdb::Service
|
|
10
|
+
format :json
|
|
11
|
+
|
|
12
|
+
require "webhookdb/service/helpers"
|
|
13
|
+
helpers Webhookdb::Service::Helpers
|
|
14
|
+
|
|
15
|
+
get :healthz do
|
|
16
|
+
Webhookdb::Postgres::Model.db.execute("SELECT 1=1")
|
|
17
|
+
status 200
|
|
18
|
+
{o: "k"}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
get :statusz do
|
|
22
|
+
status 200
|
|
23
|
+
{
|
|
24
|
+
env: Webhookdb::RACK_ENV,
|
|
25
|
+
version: Webhookdb::VERSION,
|
|
26
|
+
commit: Webhookdb::COMMIT,
|
|
27
|
+
release: Webhookdb::RELEASE,
|
|
28
|
+
log_level: Webhookdb.logger.level,
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
resource :debug do
|
|
33
|
+
get :echo do
|
|
34
|
+
pp params.to_h
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb/api"
|
|
4
|
+
|
|
5
|
+
class Webhookdb::API::WebhookSubscriptions < Webhookdb::API::V1
|
|
6
|
+
resource :organizations do
|
|
7
|
+
route_param :org_identifier, type: String do
|
|
8
|
+
resource :webhook_subscriptions do
|
|
9
|
+
desc "Return all webhook subscriptions for the given org, and all integrations."
|
|
10
|
+
get do
|
|
11
|
+
org = lookup_org!
|
|
12
|
+
subs = org.all_webhook_subscriptions
|
|
13
|
+
message = ""
|
|
14
|
+
if subs.empty?
|
|
15
|
+
message = "Organization #{org.name} has no webhook subscriptions set up.\n" \
|
|
16
|
+
"Use `webhookdb webhooks create` to set one up."
|
|
17
|
+
end
|
|
18
|
+
status 200
|
|
19
|
+
present_collection subs, with: Webhookdb::API::WebhookSubscriptionEntity, message:
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
params do
|
|
23
|
+
optional :url, prompt: "Enter the URL that WebhookDB should POST webhooks to:"
|
|
24
|
+
optional :webhook_secret, prompt: "Enter a random secret used to sign and verify webhooks to the given url:"
|
|
25
|
+
optional :service_integration_identifier,
|
|
26
|
+
type: String,
|
|
27
|
+
desc: "If provided, attach the webhook subscription to this integration rather than the org."
|
|
28
|
+
optional :service_integration_opaque_id,
|
|
29
|
+
type: String,
|
|
30
|
+
desc: "This is a deprecated parameter. In the future, please use `service_integration_identifier`."
|
|
31
|
+
end
|
|
32
|
+
post :create do
|
|
33
|
+
org = lookup_org!
|
|
34
|
+
sint = nil
|
|
35
|
+
identifier = params[:service_integration_identifier] || params[:service_integration_opaque_id]
|
|
36
|
+
sint = lookup_service_integration!(org, identifier) if identifier.present?
|
|
37
|
+
webhook_sub = Webhookdb::WebhookSubscription.create(
|
|
38
|
+
webhook_secret: params[:webhook_secret],
|
|
39
|
+
deliver_to_url: params[:url],
|
|
40
|
+
organization: sint ? nil : org,
|
|
41
|
+
service_integration: sint,
|
|
42
|
+
created_by: current_customer,
|
|
43
|
+
)
|
|
44
|
+
message = if sint
|
|
45
|
+
"All webhooks for this #{sint.service_name} integration will be sent to #{params[:url]}"
|
|
46
|
+
else
|
|
47
|
+
"All webhooks for all integrations belonging to organization #{org.name} will be sent to #{params[:url]}."
|
|
48
|
+
end
|
|
49
|
+
status 200
|
|
50
|
+
present webhook_sub, with: Webhookdb::API::WebhookSubscriptionEntity, message:
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
route_param :opaque_id, type: String do
|
|
54
|
+
helpers do
|
|
55
|
+
def lookup_sub!
|
|
56
|
+
org = lookup_org!
|
|
57
|
+
whsub = org.all_webhook_subscriptions_dataset[opaque_id: params[:opaque_id]]
|
|
58
|
+
merror!(403, "No webhook subscription with that ID exists in that organization.") if whsub.nil?
|
|
59
|
+
return whsub
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
post :test do
|
|
64
|
+
webhook_sub = lookup_sub!
|
|
65
|
+
webhook_sub.publish_immediate("test", webhook_sub.id)
|
|
66
|
+
message = "A test event has been sent to #{webhook_sub.deliver_to_url}."
|
|
67
|
+
status 200
|
|
68
|
+
present({}, with: Webhookdb::API::BaseEntity, message:)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
post :delete do
|
|
72
|
+
webhook_sub = lookup_sub!
|
|
73
|
+
webhook_sub.delete
|
|
74
|
+
message = "Events will no longer be sent to #{webhook_sub.deliver_to_url}."
|
|
75
|
+
status 200
|
|
76
|
+
present({}, with: Webhookdb::API::BaseEntity, message:)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
resource :webhook_subscriptions do
|
|
84
|
+
post :create do
|
|
85
|
+
endpoint_removed!
|
|
86
|
+
end
|
|
87
|
+
route_param :arg do
|
|
88
|
+
post :test do
|
|
89
|
+
endpoint_removed!
|
|
90
|
+
end
|
|
91
|
+
post :delete do
|
|
92
|
+
endpoint_removed!
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "grape"
|
|
4
|
+
|
|
5
|
+
require "webhookdb"
|
|
6
|
+
require "webhookdb/service"
|
|
7
|
+
|
|
8
|
+
# API is the namespace module for API resources.
|
|
9
|
+
module Webhookdb::API
|
|
10
|
+
require "webhookdb/api/entities"
|
|
11
|
+
|
|
12
|
+
class V1 < Webhookdb::Service
|
|
13
|
+
def self.inherited(subclass)
|
|
14
|
+
super
|
|
15
|
+
subclass.instance_eval do
|
|
16
|
+
version "v1", using: :path
|
|
17
|
+
format :json
|
|
18
|
+
|
|
19
|
+
require "webhookdb/service/helpers"
|
|
20
|
+
helpers Webhookdb::Service::Helpers
|
|
21
|
+
require "webhookdb/api/helpers"
|
|
22
|
+
helpers Webhookdb::API::Helpers
|
|
23
|
+
require "webhookdb/api/connstr_auth"
|
|
24
|
+
|
|
25
|
+
helpers do
|
|
26
|
+
def verified_customer!
|
|
27
|
+
c = current_customer
|
|
28
|
+
forbidden! unless c.phone_verified?
|
|
29
|
+
return c
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Lookup the organization.
|
|
33
|
+
# @param identifier [String] Can be passed in, or is extracted from the route params
|
|
34
|
+
# (:org_identifier, or :org if :org_identiier is '-').
|
|
35
|
+
# @param customer [Webhookdb::Customer] Authed customer. Will use current_customer if nil.
|
|
36
|
+
# @param allow_connstr_auth [Boolean] True to use Webhookdb::API::ConnstrAuth.
|
|
37
|
+
# See module for more details.
|
|
38
|
+
def lookup_org!(identifier=nil, customer: nil, allow_connstr_auth: false)
|
|
39
|
+
identifier ||= params[:org_identifier]
|
|
40
|
+
if identifier == "-"
|
|
41
|
+
identifier = params[:org]
|
|
42
|
+
merror!(400, "must supply 'org_identifier' or 'org' param", code: "missing_org") unless identifier
|
|
43
|
+
end
|
|
44
|
+
# Run this first to verify authentication before other lookups.
|
|
45
|
+
customer ||= allow_connstr_auth ? current_customer? : current_customer
|
|
46
|
+
# Can return multiple orgs, including ones the user cannot access
|
|
47
|
+
orgs = Webhookdb::Organization.with_identifier(identifier).all
|
|
48
|
+
merror!(403, "There is no organization with that identifier.") if orgs.empty?
|
|
49
|
+
if customer
|
|
50
|
+
# This is scoped to just orgs the user can access. We check if the identifier
|
|
51
|
+
# matches multiple orgs, in which case it's ambiguous.
|
|
52
|
+
memberships = customer.verified_memberships_dataset.where(organization: orgs).limit(2).all
|
|
53
|
+
permission_error!("You don't have permissions with that organization.") if memberships.empty?
|
|
54
|
+
merror!(500, "ambiguous", alert: true) if memberships.size > 1 # TODO: better message, tests
|
|
55
|
+
return memberships.first.organization
|
|
56
|
+
end
|
|
57
|
+
raise "something went wrong" unless allow_connstr_auth
|
|
58
|
+
org = Webhookdb::API::ConnstrAuth.find_authed(orgs, request)
|
|
59
|
+
unauthenticated! if org.nil?
|
|
60
|
+
return org
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def ensure_admin!(org=nil, customer: nil)
|
|
64
|
+
customer ||= current_customer
|
|
65
|
+
org ||= lookup_org!
|
|
66
|
+
has_no_admin = org.verified_memberships_dataset.
|
|
67
|
+
where(customer:, membership_role: Webhookdb::Role.admin_role).
|
|
68
|
+
empty?
|
|
69
|
+
permission_error!("You don't have admin privileges with #{org.name}.") if has_no_admin
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
before do
|
|
74
|
+
Sentry.configure_scope do |scope|
|
|
75
|
+
scope.set_tags(application: "public-api")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
before_validation do
|
|
80
|
+
# We want to strip control characters out of the string inputs
|
|
81
|
+
# Found the control character regex here:
|
|
82
|
+
# https://www.appsloveworld.com/ruby/100/167/how-to-remove-control-characters-in-ruby
|
|
83
|
+
#
|
|
84
|
+
rgx = /\e\[[^\x40-\x7E]*[\x40-\x7E]/
|
|
85
|
+
self.params.each do |k, v|
|
|
86
|
+
params[k] = v.gsub(rgx, "") if v.is_a?(String)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "grape"
|
|
4
|
+
require "grape-swagger"
|
|
5
|
+
require "sidekiq/web"
|
|
6
|
+
require "sidekiq/cron/web"
|
|
7
|
+
|
|
8
|
+
require "webhookdb/api"
|
|
9
|
+
require "webhookdb/async"
|
|
10
|
+
require "webhookdb/sentry"
|
|
11
|
+
require "webhookdb/service"
|
|
12
|
+
|
|
13
|
+
require "webhookdb/api/auth"
|
|
14
|
+
require "webhookdb/api/db"
|
|
15
|
+
require "webhookdb/api/demo"
|
|
16
|
+
require "webhookdb/api/install"
|
|
17
|
+
require "webhookdb/api/me"
|
|
18
|
+
require "webhookdb/api/organizations"
|
|
19
|
+
require "webhookdb/api/replay"
|
|
20
|
+
require "webhookdb/api/service_integrations"
|
|
21
|
+
require "webhookdb/api/services"
|
|
22
|
+
require "webhookdb/api/stripe"
|
|
23
|
+
require "webhookdb/api/subscriptions"
|
|
24
|
+
require "webhookdb/api/sync_targets"
|
|
25
|
+
require "webhookdb/api/system"
|
|
26
|
+
require "webhookdb/api/webhook_subscriptions"
|
|
27
|
+
|
|
28
|
+
require "webhookdb/admin_api/auth"
|
|
29
|
+
require "webhookdb/admin_api/customers"
|
|
30
|
+
require "webhookdb/admin_api/database_documents"
|
|
31
|
+
require "webhookdb/admin_api/message_deliveries"
|
|
32
|
+
require "webhookdb/admin_api/roles"
|
|
33
|
+
|
|
34
|
+
require "webterm/apps"
|
|
35
|
+
|
|
36
|
+
module Webhookdb::Apps
|
|
37
|
+
class API < Webhookdb::Service
|
|
38
|
+
mount Webhookdb::API::Auth
|
|
39
|
+
mount Webhookdb::API::Db
|
|
40
|
+
mount Webhookdb::API::Demo
|
|
41
|
+
mount Webhookdb::API::Install
|
|
42
|
+
mount Webhookdb::API::Me
|
|
43
|
+
mount Webhookdb::API::Organizations
|
|
44
|
+
mount Webhookdb::API::Replay
|
|
45
|
+
mount Webhookdb::API::ServiceIntegrations
|
|
46
|
+
mount Webhookdb::API::Services
|
|
47
|
+
mount Webhookdb::API::Stripe
|
|
48
|
+
mount Webhookdb::API::Subscriptions
|
|
49
|
+
mount Webhookdb::API::SyncTargets
|
|
50
|
+
mount Webhookdb::API::System
|
|
51
|
+
mount Webhookdb::API::WebhookSubscriptions
|
|
52
|
+
add_swagger_documentation if ENV["RACK_ENV"] == "development"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class AdminAPI < Webhookdb::Service
|
|
56
|
+
mount Webhookdb::AdminAPI::Auth
|
|
57
|
+
mount Webhookdb::AdminAPI::DatabaseDocuments
|
|
58
|
+
mount Webhookdb::AdminAPI::MessageDeliveries
|
|
59
|
+
mount Webhookdb::AdminAPI::Roles
|
|
60
|
+
mount Webhookdb::AdminAPI::Customers
|
|
61
|
+
add_swagger_documentation if ENV["RACK_ENV"] == "development"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
SidekiqWeb = Rack::Builder.new do
|
|
65
|
+
use Sentry::Rack::CaptureExceptions if Webhookdb::Sentry.enabled?
|
|
66
|
+
use Rack::Auth::Basic, "Protected Area" do |username, password|
|
|
67
|
+
# Protect against timing attacks: (https://codahale.com/a-lesson-in-timing-attacks/)
|
|
68
|
+
# - Use & (do not use &&) so that it doesn't short circuit.
|
|
69
|
+
# - Use digests to stop length information leaking
|
|
70
|
+
Rack::Utils.secure_compare(
|
|
71
|
+
::Digest::SHA256.hexdigest(username),
|
|
72
|
+
::Digest::SHA256.hexdigest(Webhookdb::Async.web_username),
|
|
73
|
+
) & Rack::Utils.secure_compare(
|
|
74
|
+
::Digest::SHA256.hexdigest(password),
|
|
75
|
+
::Digest::SHA256.hexdigest(Webhookdb::Async.web_password),
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
use Rack::Session::Cookie, secret: Webhookdb::Service.session_secret, same_site: true, max_age: 86_400
|
|
79
|
+
run Sidekiq::Web
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
Webterm = Rack::Builder.new do
|
|
83
|
+
use(Rack::SslEnforcer, {redirect_html: false}) if Webhookdb::Webterm.enforce_ssl
|
|
84
|
+
use Rack::Deflater
|
|
85
|
+
use Rack::ConditionalGet
|
|
86
|
+
use Rack::ETag
|
|
87
|
+
map "/" do
|
|
88
|
+
use Webhookdb::Webterm::RedirectIndexHtmlToRoot
|
|
89
|
+
use Webhookdb::Webterm::ServeIndexHtmlFromRoot
|
|
90
|
+
run Webhookdb::Webterm::Files
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "amigo/audit_logger"
|
|
4
|
+
require "appydays/loggable"
|
|
5
|
+
|
|
6
|
+
class Webhookdb::Async::AuditLogger < Amigo::AuditLogger
|
|
7
|
+
include Appydays::Loggable
|
|
8
|
+
|
|
9
|
+
sidekiq_options queue: "critical"
|
|
10
|
+
|
|
11
|
+
MAX_STR_LEN = 64
|
|
12
|
+
STR_PREFIX_LEN = 12
|
|
13
|
+
|
|
14
|
+
def perform(event_json)
|
|
15
|
+
j2 = event_json.dup
|
|
16
|
+
j2["payload"] = self.trim_long_strings(j2["payload"], max_str_len: MAX_STR_LEN, str_prefix_len: STR_PREFIX_LEN)
|
|
17
|
+
super(j2)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def trim_long_strings(v, max_str_len:, str_prefix_len:)
|
|
21
|
+
case v
|
|
22
|
+
when Hash
|
|
23
|
+
v.transform_values do |hv|
|
|
24
|
+
self.trim_long_strings(hv, max_str_len:, str_prefix_len:)
|
|
25
|
+
end
|
|
26
|
+
when Array
|
|
27
|
+
v.map { |item| self.trim_long_strings(item, max_str_len:, str_prefix_len:) }
|
|
28
|
+
when String
|
|
29
|
+
if v.size > max_str_len
|
|
30
|
+
v[..str_prefix_len] + "..."
|
|
31
|
+
else
|
|
32
|
+
v
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
v
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "amigo/autoscaler"
|
|
4
|
+
require "amigo/autoscaler/heroku"
|
|
5
|
+
require "webhookdb/async"
|
|
6
|
+
require "webhookdb/heroku"
|
|
7
|
+
|
|
8
|
+
module Webhookdb::Async::Autoscaler
|
|
9
|
+
include Appydays::Configurable
|
|
10
|
+
include Appydays::Loggable
|
|
11
|
+
|
|
12
|
+
AVAILABLE_PROVIDERS = ["heroku"].freeze
|
|
13
|
+
|
|
14
|
+
def self._check_provider!
|
|
15
|
+
return if AVAILABLE_PROVIDERS.include?(self.provider)
|
|
16
|
+
return if !self.enabled && self.provider.blank?
|
|
17
|
+
raise "invalid AUTOSCALER_PROVIDER: '#{self.provider}', one of: #{AVAILABLE_PROVIDERS.join(', ')}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
configurable(:autoscaler) do
|
|
21
|
+
setting :enabled, false
|
|
22
|
+
setting :provider, ""
|
|
23
|
+
setting :latency_threshold, 10
|
|
24
|
+
setting :alert_interval, 180
|
|
25
|
+
setting :poll_interval, 30
|
|
26
|
+
setting :max_additional_workers, 2
|
|
27
|
+
setting :latency_restored_threshold, 0
|
|
28
|
+
setting :hostname_regex, /^web\.1$/, convert: ->(s) { Regexp.new(s) }
|
|
29
|
+
|
|
30
|
+
after_configured do
|
|
31
|
+
self._check_provider!
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class << self
|
|
36
|
+
def enabled? = self.enabled
|
|
37
|
+
|
|
38
|
+
def start
|
|
39
|
+
raise "already started" unless @instance.nil?
|
|
40
|
+
case self.provider
|
|
41
|
+
when "heroku"
|
|
42
|
+
opts = {heroku: Webhookdb::Heroku.client, max_additional_workers: self.max_additional_workers}
|
|
43
|
+
(opts[:app_id_or_app_name] = self.heroku_app_id_or_app_name) if
|
|
44
|
+
self.heroku_app_id_or_app_name
|
|
45
|
+
(opts[:formation_id_or_formation_type] = self.heroku_formation_id_or_formation_type) if
|
|
46
|
+
self.heroku_formation_id_or_formation_type
|
|
47
|
+
@impl = Amigo::Autoscaler::Heroku.new(**opts)
|
|
48
|
+
else
|
|
49
|
+
self._check_provider!
|
|
50
|
+
end
|
|
51
|
+
@instance = Amigo::Autoscaler.new(
|
|
52
|
+
poll_interval: self.poll_interval,
|
|
53
|
+
latency_threshold: self.latency_threshold,
|
|
54
|
+
hostname_regex: self.hostname_regex,
|
|
55
|
+
handlers: [self.method(:scale_up)],
|
|
56
|
+
alert_interval: self.alert_interval,
|
|
57
|
+
latency_restored_threshold: self.latency_restored_threshold,
|
|
58
|
+
latency_restored_handlers: [self.method(:scale_down)],
|
|
59
|
+
log: ->(level, msg, kw={}) { self.logger.send(level, msg, kw) },
|
|
60
|
+
)
|
|
61
|
+
return @instance.start
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def stop
|
|
65
|
+
raise "not started" if @instance.nil?
|
|
66
|
+
@instance.stop
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def scale_up(names_and_latencies, depth:, duration:, **)
|
|
70
|
+
scale_action = @impl.scale_up(names_and_latencies, depth:, duration:, **)
|
|
71
|
+
kw = {queues: names_and_latencies, depth:, duration:, scale_action:}
|
|
72
|
+
self.logger.warn("high_latency_queues_event", **kw)
|
|
73
|
+
Sentry.with_scope do |scope|
|
|
74
|
+
scope&.set_extras(**kw)
|
|
75
|
+
Sentry.capture_message("Some queues have a high latency")
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def scale_down(depth:, duration:, **)
|
|
80
|
+
scale_action = @impl.scale_down(depth:, duration:, **)
|
|
81
|
+
self.logger.warn("high_latency_queues_resolved", depth:, duration:, scale_action:)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|