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,177 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "appydays/configurable"
|
|
4
|
+
require "appydays/loggable"
|
|
5
|
+
|
|
6
|
+
require "webhookdb"
|
|
7
|
+
|
|
8
|
+
# Keep a dynamic cache of open database connections.
|
|
9
|
+
# Very similar in behavior to Sequel::DATABASES,
|
|
10
|
+
# but we need to prune connections that have not been inactive for a while.
|
|
11
|
+
#
|
|
12
|
+
# When `borrow` is called, either a new connection is made,
|
|
13
|
+
# or an existing one used, for that URL. The connection is yield to the block.
|
|
14
|
+
#
|
|
15
|
+
# Then, after the block is called,
|
|
16
|
+
# if 'prune_interval' has elapsed since the last prune,
|
|
17
|
+
# prune all connections with 0 current connections,
|
|
18
|
+
# _other than the connection just used_.
|
|
19
|
+
# Because this connection was just used,
|
|
20
|
+
# we assume it will be used again soon.
|
|
21
|
+
#
|
|
22
|
+
# The idea here is that:
|
|
23
|
+
# - We cannot connect to the DB statically; each org can have its own DB,
|
|
24
|
+
# so storing it statically would increase DB connections to the the number of orgs in the database.
|
|
25
|
+
# - So we replace the organization/synchronization done in Sequel::DATABASES with ConnectionCache.
|
|
26
|
+
# - Any number of worker threads need access to the same DB; rather than connecting inline,
|
|
27
|
+
# which is very slow, all DB connections for an org (or across orgs if not in database isolation)
|
|
28
|
+
# can share connections via ConnectionCache.
|
|
29
|
+
# - In single-org/db environments, the active organization will always always be the same,
|
|
30
|
+
# so the connection is never returned.
|
|
31
|
+
# - In multi-org/db environments, busy orgs will likely stay busy. But a reconnect isn't the end
|
|
32
|
+
# of the world.
|
|
33
|
+
# - It seems more efficient to be pessimistic about future use, and prune anything with 0 connections,
|
|
34
|
+
# rather than optimistic, and use an LRU or something similar, since the connections are somewhat
|
|
35
|
+
# expensive resources to keep open for now reason. That said, we could switch this out for an LRU
|
|
36
|
+
# it the pessimistic pruning results in many reconnections. It would also be reasonable to increase
|
|
37
|
+
# the prune interval to avoid disconnecting as frequently.
|
|
38
|
+
#
|
|
39
|
+
# Note that, due to certain implementation details, such as setting timeouts and automatic transaction handling,
|
|
40
|
+
# we implement our own threaded connection pooling, so use the SingleThreadedConnectionPool in Sequel
|
|
41
|
+
# and manage multiple threads on our own.
|
|
42
|
+
class Webhookdb::ConnectionCache
|
|
43
|
+
include Appydays::Configurable
|
|
44
|
+
include Appydays::Loggable
|
|
45
|
+
extend Webhookdb::MethodUtilities
|
|
46
|
+
include Webhookdb::Dbutil
|
|
47
|
+
|
|
48
|
+
class ReentranceError < StandardError; end
|
|
49
|
+
|
|
50
|
+
configurable(:connection_cache) do
|
|
51
|
+
# If this many seconds has elapsed since the last connecton was borrowed,
|
|
52
|
+
# prune connections with no pending borrows.
|
|
53
|
+
setting :prune_interval, 120
|
|
54
|
+
|
|
55
|
+
# Seconds for the :fast timeout option.
|
|
56
|
+
setting :timeout_fast, 30
|
|
57
|
+
# Seconds for the :slow timeout option.
|
|
58
|
+
setting :timeout_slow, 180
|
|
59
|
+
# Seconds for the :slow_schema timeout option.
|
|
60
|
+
setting :timeout_slow_schema, 30.minutes.to_i
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
singleton_attr_accessor :_instance
|
|
64
|
+
|
|
65
|
+
def self.borrow(url, **kw, &)
|
|
66
|
+
return self._instance.borrow(url, **kw, &)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.disconnect(url)
|
|
70
|
+
self._instance.disconnect(url)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.force_disconnect_all
|
|
74
|
+
self._instance.force_disconnect_all
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
attr_accessor :dbs_for_urls, :prune_interval, :last_pruned_at
|
|
78
|
+
|
|
79
|
+
def initialize(prune_interval:)
|
|
80
|
+
@mutex = Mutex.new
|
|
81
|
+
@dbs_for_urls = {}
|
|
82
|
+
@prune_interval = prune_interval
|
|
83
|
+
@last_pruned_at = Time.now
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Connect to the database at the given URL.
|
|
87
|
+
# borrow is not re-entrant, so if the current thread already owns a connection
|
|
88
|
+
# to the given url, raise a ReentrantError.
|
|
89
|
+
# If the url has a DB not in use by any thread,
|
|
90
|
+
# yield that.
|
|
91
|
+
# If the url has no DBs opened, or all are checked out,
|
|
92
|
+
# create and yield a new connection.
|
|
93
|
+
# See class docs for more details.
|
|
94
|
+
def borrow(url, transaction: false, timeout: nil, &block)
|
|
95
|
+
raise LocalJumpError if block.nil?
|
|
96
|
+
raise ArgumentError, "url cannot be blank" if url.blank?
|
|
97
|
+
now = Time.now
|
|
98
|
+
if timeout.is_a?(Symbol)
|
|
99
|
+
timeout_name = "timeout_#{timeout}"
|
|
100
|
+
begin
|
|
101
|
+
timeout = Webhookdb::ConnectionCache.send(timeout_name)
|
|
102
|
+
rescue NoMethodError
|
|
103
|
+
raise NoMethodError, "no timeout accessor :#{timeout_name}"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
t = Thread.current
|
|
107
|
+
conn = nil
|
|
108
|
+
@mutex.synchronize do
|
|
109
|
+
db_loans = @dbs_for_urls[url] ||= {loaned: {}, available: []}
|
|
110
|
+
if db_loans[:loaned].key?(t)
|
|
111
|
+
raise ReentranceError,
|
|
112
|
+
"ConnectionCache#borrow is not re-entrant for the same database since the connection has stateful config"
|
|
113
|
+
end
|
|
114
|
+
conn = db_loans[:available].pop || take_conn(url, single_threaded: true, extensions: [:pg_json, :pg_streaming])
|
|
115
|
+
db_loans[:loaned][t] = conn
|
|
116
|
+
end
|
|
117
|
+
conn << "SET statement_timeout TO #{timeout * 1000}" if timeout.present?
|
|
118
|
+
conn << "BEGIN;" if transaction
|
|
119
|
+
begin
|
|
120
|
+
result = yield conn
|
|
121
|
+
conn << "COMMIT;" if transaction
|
|
122
|
+
rescue Sequel::DatabaseError
|
|
123
|
+
conn << "ROLLBACK;" if transaction
|
|
124
|
+
raise
|
|
125
|
+
ensure
|
|
126
|
+
conn << "SET statement_timeout TO 0" if timeout.present?
|
|
127
|
+
@mutex.synchronize do
|
|
128
|
+
@dbs_for_urls[url][:loaned].delete(t)
|
|
129
|
+
@dbs_for_urls[url][:available] << conn
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
self.prune(url) if now > self.next_prune_at
|
|
133
|
+
return result
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def next_prune_at = self.last_pruned_at + self.prune_interval
|
|
137
|
+
|
|
138
|
+
# Disconnect the cached connection for the given url,
|
|
139
|
+
# if any. In general, this is only needed when tearing down a database.
|
|
140
|
+
def disconnect(url)
|
|
141
|
+
raise ArgumentError, "url cannot be blank" if url.blank?
|
|
142
|
+
db_loans = @dbs_for_urls[url]
|
|
143
|
+
return if db_loans.nil?
|
|
144
|
+
if db_loans[:loaned].size.positive?
|
|
145
|
+
raise Webhookdb::InvalidPrecondition,
|
|
146
|
+
"url #{displaysafe_url(url)} still has #{db_loans[:loaned].size} active connections"
|
|
147
|
+
end
|
|
148
|
+
db_loans[:available].each(&:disconnect)
|
|
149
|
+
@dbs_for_urls.delete(url)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
protected def prune(skip_url)
|
|
153
|
+
@dbs_for_urls.each do |url, db_loans|
|
|
154
|
+
next false if url == skip_url
|
|
155
|
+
db_loans[:available].each(&:disconnect)
|
|
156
|
+
end
|
|
157
|
+
self.last_pruned_at = Time.now
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def force_disconnect_all
|
|
161
|
+
@dbs_for_urls.each_value do |db_loans|
|
|
162
|
+
db_loans[:available].each(&:disconnect)
|
|
163
|
+
db_loans[:loaned].each_value(&:disconnect)
|
|
164
|
+
end
|
|
165
|
+
@dbs_for_urls.clear
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def summarize
|
|
169
|
+
return self.dbs_for_urls.transform_values do |loans|
|
|
170
|
+
{loaned: loans[:loaned].size, available: loans[:available].size}
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
Webhookdb::ConnectionCache._instance = Webhookdb::ConnectionCache.new(
|
|
176
|
+
prune_interval: Webhookdb::ConnectionCache.prune_interval,
|
|
177
|
+
)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb"
|
|
4
|
+
|
|
5
|
+
module Webhookdb::Console
|
|
6
|
+
extend Webhookdb::MethodUtilities
|
|
7
|
+
|
|
8
|
+
class Error < StandardError; end
|
|
9
|
+
|
|
10
|
+
class UnsafeOperation < Error; end
|
|
11
|
+
|
|
12
|
+
class ForbiddenOperation < Error; end
|
|
13
|
+
|
|
14
|
+
singleton_attr_accessor :unsafe_mode
|
|
15
|
+
@unsafe_mode = false
|
|
16
|
+
|
|
17
|
+
singleton_attr_accessor :original_execute
|
|
18
|
+
@original_execute = nil
|
|
19
|
+
|
|
20
|
+
def self.enable_safe_mode
|
|
21
|
+
Webhookdb::Console.original_execute = Webhookdb::Postgres::Model.db.method(:execute)
|
|
22
|
+
Webhookdb::Postgres::Model.db.define_singleton_method(:execute) do |*args, &block|
|
|
23
|
+
sql = args.first
|
|
24
|
+
|
|
25
|
+
includes_where = sql.include?(" WHERE ")
|
|
26
|
+
is_update = sql.start_with?("UPDATE ")
|
|
27
|
+
is_delete = sql.start_with?("DELETE FROM")
|
|
28
|
+
|
|
29
|
+
raise ForbiddenOperation, "TRUNCATE is forbidden" if sql.start_with?("TRUNCATE TABLE")
|
|
30
|
+
|
|
31
|
+
raise ForbiddenOperation, "UPDATE without a WHERE is forbidden" if
|
|
32
|
+
is_update && !includes_where
|
|
33
|
+
|
|
34
|
+
raise ForbiddenOperation, "DELETE without a WHERE is forbidden" if
|
|
35
|
+
is_delete && !includes_where
|
|
36
|
+
|
|
37
|
+
raise UnsafeOperation, "DELETE is permitted only in an UNSAFE block" if
|
|
38
|
+
is_delete && includes_where && !Webhookdb::Console.unsafe_mode
|
|
39
|
+
|
|
40
|
+
Webhookdb::Console.original_execute.call(*args, &block)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.disable_safe_mode
|
|
45
|
+
Webhookdb::Postgres::Model.db.define_singleton_method(:execute, &Webhookdb::Console.original_execute)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.unsafe(&block)
|
|
49
|
+
raise LocalJumpError, "unsafe must be called with a block (do ... end)" unless block
|
|
50
|
+
self.unsafe_mode = true
|
|
51
|
+
begin
|
|
52
|
+
Webhookdb::Postgres::Model.db.transaction(&block)
|
|
53
|
+
ensure
|
|
54
|
+
self.unsafe_mode = false
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.console_logger(ev)
|
|
59
|
+
Webhookdb.logger.info "ConsoleLogger: [%s] %s %p" % [ev.id, ev.name, ev.payload]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
module MainMethods
|
|
63
|
+
def unsafe(&)
|
|
64
|
+
return Webhookdb::Console.unsafe(&)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def disconnect_db
|
|
68
|
+
Webhookdb::Customer.db.disconnect
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Webhookdb::Convertkit
|
|
4
|
+
include Appydays::Configurable
|
|
5
|
+
|
|
6
|
+
configurable(:convertkit) do
|
|
7
|
+
setting :http_timeout, 30
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
FIND_API_SECRET_HELP = %(- Go to https://app.convertkit.com/account_settings/advanced_settings,
|
|
11
|
+
- or from your ConvertKit Dashboard, go to your Advanced Account settings.
|
|
12
|
+
- Under the API Header you will see your API Secret, just under your API Key.
|
|
13
|
+
- Copy the API Secret.)
|
|
14
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Webhookdb::Crypto
|
|
4
|
+
def self.bin2hex(s)
|
|
5
|
+
return s.unpack1("H*")
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.cipher
|
|
9
|
+
return OpenSSL::Cipher.new("aes-256-cbc")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @return [Boxed]
|
|
13
|
+
def self.encryption_key
|
|
14
|
+
k = self.cipher.encrypt.random_key
|
|
15
|
+
return Boxed.from_raw(k)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @param key [Boxed]
|
|
19
|
+
# @param value [Boxed]
|
|
20
|
+
# @return [Boxed]
|
|
21
|
+
def self.encrypt_value(key, value)
|
|
22
|
+
cipher = self.cipher.encrypt
|
|
23
|
+
cipher.key = key.raw
|
|
24
|
+
enc = cipher.update(value.raw) + cipher.final
|
|
25
|
+
return Boxed.from_raw(enc)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @param key [Boxed]
|
|
29
|
+
# @param value [Boxed]
|
|
30
|
+
# @return [Boxed]
|
|
31
|
+
def self.decrypt_value(key, value)
|
|
32
|
+
cipher = self.cipher.decrypt
|
|
33
|
+
cipher.key = key.raw
|
|
34
|
+
dec = cipher.update(value.raw) + cipher.final
|
|
35
|
+
return Boxed.from_raw(dec)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class Boxed
|
|
39
|
+
def self.from_raw(bytestr)
|
|
40
|
+
raise ArgumentError, "bytes string cannot be nil" if bytestr.nil?
|
|
41
|
+
return self.new(bytestr, nil)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.from_b64(b64str)
|
|
45
|
+
raise ArgumentError, "base64 string cannot be nil" if b64str.nil?
|
|
46
|
+
return self.new(nil, b64str)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def initialize(raw, b64)
|
|
50
|
+
@raw = raw
|
|
51
|
+
@b64 = b64
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @return [String]
|
|
55
|
+
def base64
|
|
56
|
+
@b64 ||= Base64.urlsafe_encode64(@raw)
|
|
57
|
+
return @b64
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @return [String]
|
|
61
|
+
def raw
|
|
62
|
+
@raw ||= Base64.urlsafe_decode64(@b64)
|
|
63
|
+
return @raw
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
require "webhookdb/postgres"
|
|
6
|
+
require "webhookdb/customer"
|
|
7
|
+
|
|
8
|
+
class Webhookdb::Customer::ResetCode < Webhookdb::Postgres::Model(:customer_reset_codes)
|
|
9
|
+
class Unusable < StandardError; end
|
|
10
|
+
|
|
11
|
+
TOKEN_LENGTH = 6
|
|
12
|
+
|
|
13
|
+
plugin :timestamps
|
|
14
|
+
|
|
15
|
+
many_to_one :customer, class: Webhookdb::Customer
|
|
16
|
+
|
|
17
|
+
dataset_module do
|
|
18
|
+
def usable
|
|
19
|
+
return self.where(Sequel[used: false] & Sequel.expr { expire_at > Sequel.function(:now) })
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Invoke the given block with the reset code referred to by token.
|
|
24
|
+
# Raise Unusable if code is unusable.
|
|
25
|
+
def self.use_code_with_token(token)
|
|
26
|
+
raise LocalJumpError unless block_given?
|
|
27
|
+
|
|
28
|
+
code = self.usable[token:]
|
|
29
|
+
raise Unusable unless code&.usable?
|
|
30
|
+
|
|
31
|
+
code.db.transaction do
|
|
32
|
+
code.use!
|
|
33
|
+
yield(code)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def initialize(*)
|
|
38
|
+
super
|
|
39
|
+
self.token ||= Array.new(TOKEN_LENGTH) { rand(0..9) }.join
|
|
40
|
+
self.expire_at ||= 15.minutes.from_now
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def expire!
|
|
44
|
+
self.update(expire_at: Time.now)
|
|
45
|
+
return self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def expired?
|
|
49
|
+
return self.expire_at < Time.now
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def use!
|
|
53
|
+
now = Time.now
|
|
54
|
+
self.customer.reset_codes_dataset.usable.update(expire_at: now)
|
|
55
|
+
self.update(used: true, expire_at: now)
|
|
56
|
+
return self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def used?
|
|
60
|
+
return self.used
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def usable?
|
|
64
|
+
return false if self.used?
|
|
65
|
+
return !self.expired?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
#
|
|
69
|
+
# :section: Sequel Validation
|
|
70
|
+
#
|
|
71
|
+
|
|
72
|
+
def validate
|
|
73
|
+
super
|
|
74
|
+
self.validates_includes(["email"], :transport)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Table: customer_reset_codes
|
|
79
|
+
# -------------------------------------------------------------------------------------------------
|
|
80
|
+
# Columns:
|
|
81
|
+
# id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
|
|
82
|
+
# created_at | timestamp with time zone | NOT NULL DEFAULT now()
|
|
83
|
+
# updated_at | timestamp with time zone |
|
|
84
|
+
# transport | text | NOT NULL
|
|
85
|
+
# token | text | NOT NULL
|
|
86
|
+
# used | boolean | NOT NULL DEFAULT false
|
|
87
|
+
# expire_at | timestamp with time zone | NOT NULL
|
|
88
|
+
# customer_id | integer | NOT NULL
|
|
89
|
+
# Indexes:
|
|
90
|
+
# customer_reset_codes_pkey | PRIMARY KEY btree (id)
|
|
91
|
+
# customer_reset_codes_customer_id_index | btree (customer_id)
|
|
92
|
+
# Foreign key constraints:
|
|
93
|
+
# customer_reset_codes_customer_id_fkey | (customer_id) REFERENCES customers(id) ON DELETE CASCADE
|
|
94
|
+
# -------------------------------------------------------------------------------------------------
|