webhookdb 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,198 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "grape_entity"
|
4
|
+
|
5
|
+
require "webhookdb/service/entities"
|
6
|
+
require "webhookdb/api" unless defined? Webhookdb::API
|
7
|
+
|
8
|
+
module Webhookdb::API
|
9
|
+
MoneyEntity = Webhookdb::Service::Entities::Money
|
10
|
+
TimeRangeEntity = Webhookdb::Service::Entities::TimeRange
|
11
|
+
|
12
|
+
class BaseEntity < Webhookdb::Service::Entities::Base; end
|
13
|
+
|
14
|
+
class OrganizationEntity < BaseEntity
|
15
|
+
expose :id
|
16
|
+
expose :name
|
17
|
+
expose :key
|
18
|
+
end
|
19
|
+
|
20
|
+
class OrganizationMembershipEntity < BaseEntity
|
21
|
+
expose :customer_email, as: :email
|
22
|
+
expose :organization, with: OrganizationEntity
|
23
|
+
expose :organization_name, &self.delegate_to(:organization, :name)
|
24
|
+
expose :organization_key, &self.delegate_to(:organization, :key)
|
25
|
+
expose :status
|
26
|
+
|
27
|
+
def self.display_headers
|
28
|
+
return [
|
29
|
+
[:email, "Email"],
|
30
|
+
[:organization_name, "Organization Name"],
|
31
|
+
[:organization_key, "Organization Key"],
|
32
|
+
[:status, "Role"],
|
33
|
+
]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class CurrentCustomerEntity < BaseEntity
|
38
|
+
expose :email
|
39
|
+
expose :name
|
40
|
+
expose :default_organization, with: OrganizationEntity
|
41
|
+
expose :default_organization_formatted,
|
42
|
+
&self.delegate_to(:default_organization, :display_string, safe_with_default: "")
|
43
|
+
expose :verified_memberships, with: OrganizationMembershipEntity
|
44
|
+
expose :verified_memberships_formatted do |instance|
|
45
|
+
lines = instance.verified_memberships.map { |m| "#{m.organization.display_string}: #{m.status}" }
|
46
|
+
lines.join("\n")
|
47
|
+
end
|
48
|
+
expose :invited_memberships, as: :invitations, with: OrganizationMembershipEntity
|
49
|
+
expose :invitations_formatted do |instance|
|
50
|
+
lines = instance.invited_memberships.map { |m| "#{m.organization.display_string}: #{m.invitation_code}" }
|
51
|
+
lines.join("\n")
|
52
|
+
end
|
53
|
+
expose :display_headers do |_|
|
54
|
+
[
|
55
|
+
[:email, "Email"],
|
56
|
+
[:default_organization_formatted, "Default Org"],
|
57
|
+
[:verified_memberships_formatted, "Memberships"],
|
58
|
+
[:invitations_formatted, "Invitations"],
|
59
|
+
]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class ServiceIntegrationEntity < BaseEntity
|
64
|
+
expose :opaque_id
|
65
|
+
expose :service_name
|
66
|
+
expose :table_name
|
67
|
+
|
68
|
+
def self.display_headers
|
69
|
+
return [[:service_name, "Name"], [:table_name, "Table"], [:opaque_id, "Id"]]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class ServiceEntity < BaseEntity
|
74
|
+
expose :name
|
75
|
+
|
76
|
+
def self.display_headers
|
77
|
+
return [[:name, "Name"]]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class StateMachineEntity < BaseEntity
|
82
|
+
expose :needs_input
|
83
|
+
expose :prompt
|
84
|
+
expose :prompt_is_secret
|
85
|
+
expose :post_to_url
|
86
|
+
expose :post_params
|
87
|
+
expose :post_params_value_key
|
88
|
+
expose :complete
|
89
|
+
expose :output
|
90
|
+
expose :error_code
|
91
|
+
expose :extras do |_, opts|
|
92
|
+
opts[:extras] || {}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class SubscriptionPlanEntity < BaseEntity
|
97
|
+
expose :key
|
98
|
+
expose :description
|
99
|
+
expose :price, with: MoneyEntity
|
100
|
+
expose :price_formatted, &self.delegate_to(:price, :format)
|
101
|
+
|
102
|
+
def self.display_headers
|
103
|
+
return [[:key, "Key"], [:description, "Description"], [:price_formatted, "Price"]]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class WebhookSubscriptionEntity < BaseEntity
|
108
|
+
expose :created_at
|
109
|
+
expose :opaque_id
|
110
|
+
expose :deliver_to_url
|
111
|
+
expose :organization, with: OrganizationEntity
|
112
|
+
expose :service_integration, with: ServiceIntegrationEntity
|
113
|
+
expose :associated_type
|
114
|
+
expose :associated_id
|
115
|
+
expose :status
|
116
|
+
|
117
|
+
def self.display_headers
|
118
|
+
return [
|
119
|
+
[:opaque_id, "Id"],
|
120
|
+
[:deliver_to_url, "Url"],
|
121
|
+
[:status, "Status"],
|
122
|
+
[:associated_type, "Associated Type"],
|
123
|
+
[:associated_id, "Associated Id"],
|
124
|
+
]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class BaseSyncTargetEntity < BaseEntity
|
129
|
+
expose :created_at
|
130
|
+
expose :opaque_id
|
131
|
+
expose :service_integration, with: ServiceIntegrationEntity
|
132
|
+
expose :period_seconds
|
133
|
+
expose :displaysafe_connection_url, as: :connection_url
|
134
|
+
expose :table
|
135
|
+
expose :schema
|
136
|
+
expose :last_synced_at
|
137
|
+
expose :associated_type
|
138
|
+
expose :associated_id
|
139
|
+
expose :associated_object_display
|
140
|
+
end
|
141
|
+
|
142
|
+
class DbSyncTargetEntity < BaseSyncTargetEntity
|
143
|
+
expose :schema_and_table_string
|
144
|
+
|
145
|
+
def self.display_headers
|
146
|
+
return [
|
147
|
+
[:opaque_id, "Id"],
|
148
|
+
[:connection_url, "URL"],
|
149
|
+
[:associated_object_display, "Associated"],
|
150
|
+
[:schema_and_table_string, "Table"],
|
151
|
+
[:last_synced_at, "Last Synced"],
|
152
|
+
[:period_seconds, "Period"],
|
153
|
+
[:page_size, "Page"],
|
154
|
+
]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class HttpSyncTargetEntity < BaseSyncTargetEntity
|
159
|
+
def self.display_headers
|
160
|
+
return [
|
161
|
+
[:opaque_id, "Id"],
|
162
|
+
[:connection_url, "URL"],
|
163
|
+
[:associated_object_display, "Associated"],
|
164
|
+
[:last_synced_at, "Last Synced"],
|
165
|
+
[:period_seconds, "Period"],
|
166
|
+
[:page_size, "Page"],
|
167
|
+
]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class DatabaseMigrationEntity < BaseEntity
|
172
|
+
expose :created_at
|
173
|
+
expose :started_at
|
174
|
+
expose :finished_at
|
175
|
+
expose :displaysafe_source_url, as: :source_url
|
176
|
+
expose :displaysafe_destination_url, as: :destination_url
|
177
|
+
expose :status
|
178
|
+
|
179
|
+
def self.display_headers
|
180
|
+
return [
|
181
|
+
[:created_at, "Created at"],
|
182
|
+
[:started_at, "Started at"],
|
183
|
+
[:finished_at, "Finished at"],
|
184
|
+
[:source_url, "Source"],
|
185
|
+
[:destination_url, "Destination"],
|
186
|
+
[:status, "Status"],
|
187
|
+
]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
class BackfillJobEntity < BaseEntity
|
192
|
+
expose :opaque_id, as: :id
|
193
|
+
expose :status
|
194
|
+
expose :started_at
|
195
|
+
expose :fully_finished_at, as: :finished_at
|
196
|
+
expose :service_integration, with: ServiceIntegrationEntity
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,253 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "grape"
|
4
|
+
require "webhookdb/demo_mode"
|
5
|
+
require "webhookdb/jobs/process_webhook"
|
6
|
+
|
7
|
+
module Webhookdb::API::Helpers
|
8
|
+
extend Grape::API::Helpers
|
9
|
+
|
10
|
+
# Prompt for input given some criteria.
|
11
|
+
#
|
12
|
+
# NOTE: You cannot use :prompt with 'requires', or 'allow_blank'.
|
13
|
+
# You MUST use 'optional', and not specify allow_blank
|
14
|
+
# (validation will error if these conditions are not met).
|
15
|
+
# The semantics around prompts are too subtle to use these built-in validators;
|
16
|
+
# we must use our own.
|
17
|
+
# Note also, while :default will not error (because we cannot detect it easily),
|
18
|
+
# it should not be used since it defeats the purpose of :prompt.
|
19
|
+
#
|
20
|
+
# The main complexity is around optional params
|
21
|
+
# that will take a given default if blank (but we need to prompt if not supplied),
|
22
|
+
# or 'enter to confirm' type prompts, where we want to prompt if not supplied.
|
23
|
+
#
|
24
|
+
# To handle these cases, use the :optional and :confirm options, as below.
|
25
|
+
# On the Go client, use string pointers for these values, with omitempty;
|
26
|
+
# they will still submit an empty string, but will not include the key if nil.
|
27
|
+
#
|
28
|
+
# Examples:
|
29
|
+
#
|
30
|
+
# requires :param, prompt: "Enter a value:"
|
31
|
+
#
|
32
|
+
# This is by far the most common usage.
|
33
|
+
# If :param is not present (ie, `params[:param].present?` is falsy),
|
34
|
+
# a 422 is returned with a StateMachineStep with a prompt value of 'Enter a value:'.
|
35
|
+
#
|
36
|
+
# All other forms use a Hash instead of a string as the argument.
|
37
|
+
#
|
38
|
+
# requires :param, prompt: {message: 'Enter secret:', secret: true}
|
39
|
+
#
|
40
|
+
# Same as above, but the prompt is set to be a secret.
|
41
|
+
#
|
42
|
+
# requires :param, prompt: {message: 'Press Enter to confirm, or Ctrl+C to cancel:', confirm: true}
|
43
|
+
#
|
44
|
+
# This will 422 if :param is not provided or is nil;
|
45
|
+
# it will pass otherwise (so empty values, like '' or false, are valid).
|
46
|
+
# This is used to guard against actions that need confirmation.
|
47
|
+
# Note that in many situations, we don't know about the need to confirm
|
48
|
+
# until we're into the body of the endpoint. In these cases,
|
49
|
+
# use `Webhookdb::API::Helpers.prompt_for_required_param!` directly,
|
50
|
+
# along with something like `optional :param`.
|
51
|
+
#
|
52
|
+
# requires :param, prompt: {message: 'This will default', optional: true}
|
53
|
+
#
|
54
|
+
# This will 422 if not provided, but will pass otherwise
|
55
|
+
# (so nil, '', and false are all valid values).
|
56
|
+
#
|
57
|
+
# optional :param, prompt: {message: 'Bypassable', disable: ->(req) { req.env['HTTP_NOPROMPT']} }
|
58
|
+
#
|
59
|
+
# This will disable the prompt behavior if the proc given to :disable returns true.
|
60
|
+
# This is mostly useful when we want to avoid prompting for something
|
61
|
+
# because the endpoint is going to have some particular behavior that will avoid the purpose of the prompt.
|
62
|
+
#
|
63
|
+
class Prompt < ::Grape::Validations::Validators::Base
|
64
|
+
def validate(request)
|
65
|
+
raise "allow_blank must not be set" unless @allow_blank.nil?
|
66
|
+
attr_name = @attrs.first
|
67
|
+
if @option.is_a?(String)
|
68
|
+
options = {message: @option}
|
69
|
+
else
|
70
|
+
options = @option
|
71
|
+
raise "Missing :message key in prompt args" unless options[:message]
|
72
|
+
end
|
73
|
+
raise "must use optional for #{attr_name}" if @required
|
74
|
+
return unless self.needs_prompt?(attr_name, request, options)
|
75
|
+
Webhookdb::API::Helpers.prompt_for_required_param!(
|
76
|
+
request,
|
77
|
+
attr_name,
|
78
|
+
options[:message],
|
79
|
+
secret: options[:secret] || false,
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
protected def needs_prompt?(attr_name, request, options)
|
84
|
+
if (disable_proc = options[:disable]) && (disable_proc[request])
|
85
|
+
return false
|
86
|
+
end
|
87
|
+
options[:demo_mode_proc][request] if Webhookdb::DemoMode.client_enabled? && options[:demo_mode_proc]
|
88
|
+
params = request.params
|
89
|
+
if options[:confirm]
|
90
|
+
return true unless params.key?(attr_name)
|
91
|
+
return true if params[attr_name].nil?
|
92
|
+
return false
|
93
|
+
end
|
94
|
+
if options[:optional]
|
95
|
+
return true unless params.key?(attr_name)
|
96
|
+
return false
|
97
|
+
end
|
98
|
+
value = params[attr_name]
|
99
|
+
return false if value.present?
|
100
|
+
return false if value.is_a?(FalseClass)
|
101
|
+
return true
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.prompt_for_required_param!(request, key, prompt, secret: false, output: "")
|
106
|
+
step = Webhookdb::Replicator::StateMachineStep.new
|
107
|
+
step.output = output
|
108
|
+
step.post_to_url = request.path
|
109
|
+
step.post_params = request.params.to_h
|
110
|
+
step.post_params_value_key = key
|
111
|
+
step.set_prompt(prompt, secret:)
|
112
|
+
body = Webhookdb::Service.error_body(
|
113
|
+
422,
|
114
|
+
"Prompt for required params",
|
115
|
+
code: "prompt_required_params",
|
116
|
+
more: {state_machine_step: Webhookdb::API::StateMachineEntity.represent(step)},
|
117
|
+
)
|
118
|
+
throw :error, message: body, status: 422, headers: {"Whdb-Prompt" => key.to_s}
|
119
|
+
end
|
120
|
+
|
121
|
+
def lookup_service_integration!(org, identifier)
|
122
|
+
sints = org.service_integrations_dataset.
|
123
|
+
where(Sequel[service_name: identifier] | Sequel[table_name: identifier] | Sequel[opaque_id: identifier]).
|
124
|
+
limit(2).all
|
125
|
+
return sints.first if sints.size == 1
|
126
|
+
merror!(403, "There is no service integration with that identifier.") if sints.empty?
|
127
|
+
dupe_attr = nil
|
128
|
+
alternative = nil
|
129
|
+
if sints.first.service_name == identifier
|
130
|
+
dupe_attr = "service name"
|
131
|
+
alternative = "table name"
|
132
|
+
else
|
133
|
+
dupe_attr = "table name"
|
134
|
+
alternative = "service name"
|
135
|
+
end
|
136
|
+
msg403 = "There are multiple integrations with that #{dupe_attr}. " \
|
137
|
+
"Try again using an integration id, or a #{alternative}. " \
|
138
|
+
"Use `webhookdb integrations list` to see all integrations."
|
139
|
+
merror!(409, msg403)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Our primary webhook endpoint is /v1/service_integrations/<opaque_id>,
|
143
|
+
# but in some cases we need a 'static' endpoint for apps to send to,
|
144
|
+
# like /v1/install/front/webhooks.
|
145
|
+
# Those endpoints share the webhook handling behavior with this method.
|
146
|
+
#
|
147
|
+
# The block passed to this method yields the service integration.
|
148
|
+
# This is important because we want to make sure to log the webhook if something goes wrong
|
149
|
+
# while looking up the service integration.
|
150
|
+
#
|
151
|
+
# The potential_opaque_id should be a way to identify who is responsible for
|
152
|
+
# the webhook request. For example, to `/v1/service_integrations/svi_abc`,
|
153
|
+
# this would be `svi_abc` (even though it's an invalid opaque id).
|
154
|
+
# In other cases, especially marketplace integrations, this could be some other value
|
155
|
+
# to identify the webhook that was sent.
|
156
|
+
#
|
157
|
+
# If the block yields the Symbol :pass, no further handling is done;
|
158
|
+
# this would be done for example when there is no valid service integration.
|
159
|
+
# Otherwise, the block must yield a service integration.
|
160
|
+
def handle_webhook_request(potential_opaque_id, &)
|
161
|
+
opaque_id = potential_opaque_id
|
162
|
+
organization_id = nil
|
163
|
+
s_status = nil
|
164
|
+
request_headers = {}
|
165
|
+
raise LocalJumpError unless block_given?
|
166
|
+
begin
|
167
|
+
sint = yield
|
168
|
+
return if sint == :pass
|
169
|
+
raise "error instead of return nil if there is no service integration" if sint.nil?
|
170
|
+
opaque_id = sint.opaque_id
|
171
|
+
organization_id = sint.organization_id
|
172
|
+
request_headers = request.headers.dup
|
173
|
+
if (content_type = env["CONTENT_TYPE"])
|
174
|
+
request_headers["Content-Type"] = content_type
|
175
|
+
end
|
176
|
+
svc = Webhookdb::Replicator.create(sint).dispatch_request_to(request)
|
177
|
+
svc.preprocess_headers_for_logging(request_headers)
|
178
|
+
handling_sint = svc.service_integration
|
179
|
+
whresp = svc.webhook_response(request)
|
180
|
+
s_status, s_headers, s_body = whresp.to_rack
|
181
|
+
(s_status = 200) if s_status >= 400 && Webhookdb.regression_mode?
|
182
|
+
|
183
|
+
if s_status >= 400
|
184
|
+
logger.warn "rejected_webhook", webhook_headers: request_headers, webhook_body: env["api.request.body"]
|
185
|
+
header "Whdb-Rejected-Reason", whresp.reason
|
186
|
+
else
|
187
|
+
req_body = env.key?("api.request.body") ? env["api.request.body"] : env["rack.input"].read
|
188
|
+
req_body = {} if req_body.blank?
|
189
|
+
process_kwargs = {
|
190
|
+
headers: request_headers,
|
191
|
+
body: req_body,
|
192
|
+
request_path: request.path_info,
|
193
|
+
request_method: request.request_method,
|
194
|
+
}
|
195
|
+
event_json = Amigo::Event.create(
|
196
|
+
"webhookdb.serviceintegration.webhook", [handling_sint.id, process_kwargs],
|
197
|
+
).as_json
|
198
|
+
# Audit Log this synchronously.
|
199
|
+
# It should be fast enough. We may as well log here so we can avoid
|
200
|
+
# serializing the (large) webhook payload multiple times, as with normal pubsub.
|
201
|
+
Webhookdb::Async::AuditLogger.new.perform(event_json)
|
202
|
+
if svc.process_webhooks_synchronously? || Webhookdb::Replicator.always_process_synchronously
|
203
|
+
whreq = Webhookdb::Replicator::WebhookRequest.new(
|
204
|
+
method: process_kwargs[:request_method],
|
205
|
+
path: process_kwargs[:request_path],
|
206
|
+
headers: process_kwargs[:headers],
|
207
|
+
body: process_kwargs[:body],
|
208
|
+
)
|
209
|
+
inserted = svc.upsert_webhook(whreq)
|
210
|
+
s_body = svc.synchronous_processing_response_body(upserted: inserted, request: whreq)
|
211
|
+
else
|
212
|
+
queue = svc.upsert_has_deps? ? "netout" : "webhook"
|
213
|
+
Webhookdb::Jobs::ProcessWebhook.set(queue:).perform_async(event_json)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
s_headers.each { |k, v| header k, v }
|
218
|
+
if s_headers["Content-Type"] == "application/json"
|
219
|
+
body Oj.load(s_body)
|
220
|
+
else
|
221
|
+
env["api.format"] = :binary
|
222
|
+
body s_body
|
223
|
+
end
|
224
|
+
status s_status
|
225
|
+
ensure
|
226
|
+
_log_webhook_request(opaque_id, organization_id, s_status, request_headers)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def _log_webhook_request(opaque_id, organization_id, sstatus, request_headers)
|
231
|
+
return if request.headers[Webhookdb::LoggedWebhook::RETRY_HEADER]
|
232
|
+
# Status can be set from:
|
233
|
+
# - the 'status' method, which will be 201 if it hasn't been set,
|
234
|
+
# or another value if it has been set.
|
235
|
+
# - the webhook responder, which could respond with 401, etc
|
236
|
+
# - if there was an exception- so no status is set yet- use 0
|
237
|
+
# The main thing to watch out for is that we:
|
238
|
+
# - Cannot assume an exception is a 500 (it can be rescued later)
|
239
|
+
# - Must handle error! calls
|
240
|
+
# Anyway, this is all pretty confusing, but it's all tested.
|
241
|
+
rstatus = status == 201 ? (sstatus || 0) : status
|
242
|
+
request.body.rewind
|
243
|
+
Webhookdb::LoggedWebhook.resilient_insert(
|
244
|
+
request_body: request.body.read,
|
245
|
+
request_headers: request_headers.to_json,
|
246
|
+
request_method: request.request_method,
|
247
|
+
request_path: request.path_info,
|
248
|
+
response_status: rstatus,
|
249
|
+
organization_id:,
|
250
|
+
service_integration_opaque_id: opaque_id,
|
251
|
+
)
|
252
|
+
end
|
253
|
+
end
|