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,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "webhookdb/postgres"
|
|
4
|
+
|
|
5
|
+
module Webhookdb::Postgres::Validations
|
|
6
|
+
# Ensures that only one of the passed columns is not nil.
|
|
7
|
+
def validates_mutually_exclusive(*cols, predicate: :nil?)
|
|
8
|
+
truthy_cols = cols.find_all { |col| !self[col].send(predicate) }
|
|
9
|
+
multiple_set = truthy_cols.length > 1
|
|
10
|
+
return unless multiple_set
|
|
11
|
+
self.errors.add(truthy_cols.first, "is mutually exclusive with other set columns #{truthy_cols[1..].join(', ')}")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Ensures that either all of the passed columns are nil or none of them are.
|
|
15
|
+
def validates_all_or_none(*cols, predicate: :nil?)
|
|
16
|
+
truthy_cols = cols.find_all { |col| !self[col].send(predicate) }
|
|
17
|
+
|
|
18
|
+
return if truthy_cols.empty? || truthy_cols.length == cols.length
|
|
19
|
+
|
|
20
|
+
msg = "the columns #{cols[1..].join(', ')} must all be set or all be #{predicate.to_s.delete_suffix('?')}"
|
|
21
|
+
self.errors.add(cols.first, msg)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Ensures that at least one of the passed columns is not nil.
|
|
25
|
+
def validates_at_least_one_of(*cols, predicate: :nil?)
|
|
26
|
+
any_truthy = cols.any? { |col| !self[col].send(predicate) }
|
|
27
|
+
return if any_truthy
|
|
28
|
+
msg = "at least one of #{cols.join(', ')} must be not #{predicate.to_s.delete_suffix('?')}"
|
|
29
|
+
self.errors.add(cols.first, msg)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Ensures that one and only one of the passed columns is not nil.
|
|
33
|
+
def validates_exactly_one_of(*cols, predicate: :nil?)
|
|
34
|
+
self.validates_at_least_one_of(*cols, predicate:)
|
|
35
|
+
self.validates_mutually_exclusive(*cols, predicate:)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def validates_ip_address(col)
|
|
39
|
+
return if self[col].respond_to?(:ipv4?)
|
|
40
|
+
begin
|
|
41
|
+
IPAddr.new(self[col])
|
|
42
|
+
rescue IPAddr::Error
|
|
43
|
+
self.errors.add(col, "is not a valid INET address")
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pg"
|
|
4
|
+
require "appydays/loggable"
|
|
5
|
+
require "sequel"
|
|
6
|
+
require "sequel/core"
|
|
7
|
+
require "sequel/adapters/postgres"
|
|
8
|
+
|
|
9
|
+
require "appydays/loggable/sequel_logger"
|
|
10
|
+
require "webhookdb"
|
|
11
|
+
|
|
12
|
+
module Webhookdb::Postgres
|
|
13
|
+
extend Webhookdb::MethodUtilities
|
|
14
|
+
include Appydays::Loggable
|
|
15
|
+
|
|
16
|
+
class InTransaction < StandardError; end
|
|
17
|
+
|
|
18
|
+
singleton_attr_accessor :unsafe_skip_transaction_check
|
|
19
|
+
@unsafe_skip_transaction_check = false
|
|
20
|
+
def self.check_transaction(db, error_msg)
|
|
21
|
+
return true if self.unsafe_skip_transaction_check
|
|
22
|
+
return true unless db.in_transaction?
|
|
23
|
+
raise InTransaction, error_msg
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Sequel API -- load some global extensions and plugins
|
|
27
|
+
Sequel.extension(
|
|
28
|
+
:core_extensions,
|
|
29
|
+
:core_refinements,
|
|
30
|
+
:is_distinct_from,
|
|
31
|
+
:pg_array,
|
|
32
|
+
:pg_array_ops,
|
|
33
|
+
:pg_inet,
|
|
34
|
+
:pg_inet_ops,
|
|
35
|
+
:pg_json,
|
|
36
|
+
:pg_json_ops,
|
|
37
|
+
:pg_range,
|
|
38
|
+
:pg_range_ops,
|
|
39
|
+
:symbol_as_refinement,
|
|
40
|
+
)
|
|
41
|
+
Sequel::Model.plugin(:force_encoding, "UTF-8")
|
|
42
|
+
|
|
43
|
+
# Require paths for model superclasses.
|
|
44
|
+
SUPERCLASSES = [
|
|
45
|
+
"webhookdb/postgres/model",
|
|
46
|
+
].freeze
|
|
47
|
+
|
|
48
|
+
# Require paths for all Sequel models used by the app.
|
|
49
|
+
MODELS = [
|
|
50
|
+
"webhookdb/backfill_job",
|
|
51
|
+
"webhookdb/customer",
|
|
52
|
+
"webhookdb/customer/reset_code",
|
|
53
|
+
"webhookdb/database_document",
|
|
54
|
+
"webhookdb/idempotency",
|
|
55
|
+
"webhookdb/logged_webhook",
|
|
56
|
+
"webhookdb/message/body",
|
|
57
|
+
"webhookdb/message/delivery",
|
|
58
|
+
"webhookdb/oauth/session",
|
|
59
|
+
"webhookdb/organization",
|
|
60
|
+
"webhookdb/organization/database_migration",
|
|
61
|
+
"webhookdb/organization_membership",
|
|
62
|
+
"webhookdb/role",
|
|
63
|
+
"webhookdb/service_integration",
|
|
64
|
+
"webhookdb/subscription",
|
|
65
|
+
"webhookdb/sync_target",
|
|
66
|
+
"webhookdb/webhook_subscription",
|
|
67
|
+
"webhookdb/webhook_subscription/delivery",
|
|
68
|
+
].freeze
|
|
69
|
+
|
|
70
|
+
# If true, deferred model events publish immediately.
|
|
71
|
+
# Should only be true for some types of testing.
|
|
72
|
+
singleton_predicate_accessor :do_not_defer_events
|
|
73
|
+
|
|
74
|
+
# The list of model superclasses (that get their own database connections)
|
|
75
|
+
singleton_attr_reader :model_superclasses
|
|
76
|
+
@model_superclasses = Set.new
|
|
77
|
+
|
|
78
|
+
### Register the given +superclass+ as a base class for a set of models, for operations
|
|
79
|
+
### which should happen on all the current database connections.
|
|
80
|
+
def self.register_model_superclass(superclass)
|
|
81
|
+
self.logger.debug "Registered model superclass: %p" % [superclass]
|
|
82
|
+
self.model_superclasses << superclass
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
##
|
|
86
|
+
# The list of models that will be required once the database connection has been established.
|
|
87
|
+
singleton_attr_reader :registered_models
|
|
88
|
+
@registered_models = Set.new
|
|
89
|
+
|
|
90
|
+
### Add a +path+ to require once the database connection is set.
|
|
91
|
+
def self.register_model(path)
|
|
92
|
+
self.logger.debug "Registered model for requiring: %s" % [path]
|
|
93
|
+
|
|
94
|
+
# If the connection's set, require the path immediately.
|
|
95
|
+
if self.model_superclasses.any?(&:db)
|
|
96
|
+
Appydays::Loggable[self].silence(:fatal) do
|
|
97
|
+
require(path)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
self.registered_models << path
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
### Require the model classes once the database connection has been established
|
|
105
|
+
def self.require_models
|
|
106
|
+
self.registered_models.each do |path|
|
|
107
|
+
require path
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
### Call the block for each registered model superclass.
|
|
112
|
+
def self.each_model_superclass(&)
|
|
113
|
+
self.model_superclasses.each(&)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def self.each_model_class(&)
|
|
117
|
+
self.each_model_superclass do |sc|
|
|
118
|
+
sc.descendants.each(&)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def self.run_all_migrations(target: nil)
|
|
123
|
+
Sequel.extension :migration
|
|
124
|
+
Webhookdb::Postgres.each_model_superclass do |cls|
|
|
125
|
+
cls.install_all_extensions
|
|
126
|
+
Sequel::Migrator.run(cls.db, Pathname(__FILE__).dirname.parent.parent + "db/migrations", target:)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# We can always register the models right away, since it does not have a side effect.
|
|
131
|
+
MODELS.each do |m|
|
|
132
|
+
self.register_model(m)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# After configuration, load superclasses. You may need these without loading models,
|
|
136
|
+
# like if you need access to their DBs without loading them
|
|
137
|
+
# (if their tables do not yet exist)
|
|
138
|
+
def self.load_superclasses
|
|
139
|
+
SUPERCLASSES.each do |sc|
|
|
140
|
+
require(sc)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# After configuration, require in the model superclass files,
|
|
145
|
+
# to make sure their .db gets set and they're in model_superclasses.
|
|
146
|
+
def self.load_models
|
|
147
|
+
self.load_superclasses
|
|
148
|
+
Appydays::Loggable[self].silence(:fatal) do
|
|
149
|
+
self.require_models
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Return 'Time.now' as an expression suitable for Sequel/SQL.
|
|
154
|
+
# In some cases (like range @> expressions) you need to cast to a timestamptz explicitly,
|
|
155
|
+
# the implicit cast isn't enough.
|
|
156
|
+
# And because 'Time.now' is an external dependency, we should always use Sequel.delay,
|
|
157
|
+
# to avoid any internal caching it will do,
|
|
158
|
+
# like in association blocks: https://github.com/jeremyevans/sequel/blob/master/doc/association_basics.rdoc#block-
|
|
159
|
+
def self.now_sql(&block)
|
|
160
|
+
block ||= -> { Time.now }
|
|
161
|
+
return Sequel.delay { Sequel.cast(block.call, :timestamptz) }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Call block immediately if not deferring events; otherwise call it after db commit.
|
|
165
|
+
def self.defer_after_commit(db, &block)
|
|
166
|
+
raise LocalJumpError unless block
|
|
167
|
+
return yield if self.do_not_defer_events?
|
|
168
|
+
return db.after_commit(&block)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def self.defer_after_rollback(db, &block)
|
|
172
|
+
raise LocalJumpError unless block
|
|
173
|
+
return yield if self.do_not_defer_events?
|
|
174
|
+
return db.after_rollback(&block)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "appydays/configurable"
|
|
4
|
+
|
|
5
|
+
module Webhookdb::Postmark
|
|
6
|
+
include Appydays::Configurable
|
|
7
|
+
|
|
8
|
+
configurable(:postmark) do
|
|
9
|
+
# See https://postmarkapp.com/support/article/800-ips-for-firewalls#webhooks
|
|
10
|
+
setting :allowed_ips,
|
|
11
|
+
["127.0.0.1", "3.134.147.250", "50.31.156.6", "50.31.156.77", "18.217.206.57"],
|
|
12
|
+
convert: ->(s) { s.split.map(&:strip) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.webhook_response(request)
|
|
16
|
+
ip = request.ip
|
|
17
|
+
allowed = self.allowed_ips.include?(ip)
|
|
18
|
+
return allowed ? Webhookdb::WebhookResponse.ok : Webhookdb::WebhookResponse.error("invalid ip")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "appydays/configurable"
|
|
4
|
+
require "redis_client"
|
|
5
|
+
|
|
6
|
+
module Webhookdb::Redis
|
|
7
|
+
include Appydays::Configurable
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
attr_accessor :cache
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
configurable(:redis) do
|
|
14
|
+
setting :cache_url, "redis://localhost:6379/0"
|
|
15
|
+
setting :cache_url_provider, "REDIS_URL"
|
|
16
|
+
setting :verify_ssl, false
|
|
17
|
+
|
|
18
|
+
after_configured do
|
|
19
|
+
url = ENV.fetch(self.cache_url_provider, self.cache_url)
|
|
20
|
+
cache_params = {url:, reconnect_attempts: 1}
|
|
21
|
+
cache_params[:ssl_params] = {verify_mode: OpenSSL::SSL::VERIFY_NONE} unless
|
|
22
|
+
self.verify_ssl
|
|
23
|
+
redis_config = RedisClient.config(**cache_params)
|
|
24
|
+
self.cache = redis_config.new_pool(
|
|
25
|
+
timeout: Webhookdb::Dbutil.pool_timeout,
|
|
26
|
+
size: Webhookdb::Dbutil.max_connections,
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.cache_key(parts)
|
|
32
|
+
tail = parts.join("/")
|
|
33
|
+
return "cache/#{tail}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "down"
|
|
4
|
+
|
|
5
|
+
require "webhookdb/xml"
|
|
6
|
+
|
|
7
|
+
class Webhookdb::Replicator::AtomSingleFeedV1 < Webhookdb::Replicator::Base
|
|
8
|
+
include Appydays::Loggable
|
|
9
|
+
|
|
10
|
+
# @return [Webhookdb::Replicator::Descriptor]
|
|
11
|
+
def self.descriptor
|
|
12
|
+
return Webhookdb::Replicator::Descriptor.new(
|
|
13
|
+
name: "atom_single_feed_v1",
|
|
14
|
+
ctor: ->(sint) { Webhookdb::Replicator::AtomSingleFeedV1.new(sint) },
|
|
15
|
+
feature_roles: [],
|
|
16
|
+
resource_name_singular: "Atom Single Feed",
|
|
17
|
+
supports_backfill: true,
|
|
18
|
+
description: "Convert any Atom XML feed into a database table for querying and persistent archiving.",
|
|
19
|
+
api_docs_url: "https://en.wikipedia.org/wiki/Atom_(web_standard)",
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def _remote_key_column
|
|
24
|
+
return Webhookdb::Replicator::Column.new(:entry_id, TEXT, data_key: "id")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
CONV_GEO_LAT = Webhookdb::Replicator::Column.converter_array_element(index: 0, sep: " ", cls: DECIMAL)
|
|
28
|
+
CONV_GEO_LNG = Webhookdb::Replicator::Column.converter_array_element(index: 1, sep: " ", cls: DECIMAL)
|
|
29
|
+
|
|
30
|
+
def _denormalized_columns
|
|
31
|
+
col = Webhookdb::Replicator::Column
|
|
32
|
+
return [
|
|
33
|
+
col.new(:row_created_at, TIMESTAMP, index: true, optional: true, defaulter: :now),
|
|
34
|
+
col.new(:updated, TIMESTAMP, index: true),
|
|
35
|
+
col.new(:title, TEXT),
|
|
36
|
+
col.new(:published, TIMESTAMP, index: true, optional: true),
|
|
37
|
+
col.new(:geo_lat, DECIMAL, data_key: "georss:point", optional: true, converter: CONV_GEO_LAT),
|
|
38
|
+
col.new(:geo_lng, DECIMAL, data_key: "georss:point", optional: true, converter: CONV_GEO_LNG),
|
|
39
|
+
]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def _timestamp_column_name
|
|
43
|
+
return :updated
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def _resource_and_event(request)
|
|
47
|
+
return request.body, nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def _update_where_expr
|
|
51
|
+
return self.qualified_table_sequel_identifier[:updated] < Sequel[:excluded][:updated]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def _upsert_update_expr(inserting, **_kwargs)
|
|
55
|
+
update = super
|
|
56
|
+
# Only set created_at if it's not set so the initial insert isn't modified.
|
|
57
|
+
self._coalesce_excluded_on_update(update, [:row_created_at])
|
|
58
|
+
return update
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def _fetch_backfill_page(*)
|
|
62
|
+
io = Webhookdb::Http.get(self.service_integration.api_url, logger: self.logger, timeout: 30)
|
|
63
|
+
feed_obj = Webhookdb::Xml::Atom.parse(io.body)
|
|
64
|
+
return feed_obj.fetch("entries"), nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def _verify_backfill_err_msg
|
|
68
|
+
return "Sorry, we can't reach that URL. Please double check it and try again."
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def _backfillers
|
|
72
|
+
return [Backfiller.new(self)]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class Backfiller < Webhookdb::Replicator::Base::ServiceBackfiller
|
|
76
|
+
include Webhookdb::Backfiller::Bulk
|
|
77
|
+
attr_reader :upserting_replicator
|
|
78
|
+
|
|
79
|
+
def initialize(replicator)
|
|
80
|
+
super
|
|
81
|
+
@upserting_replicator = @svc
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def upsert_page_size = 500
|
|
85
|
+
def conditional_upsert? = true
|
|
86
|
+
def prepare_body(body) = body
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def _webhook_response(_request)
|
|
90
|
+
return Webhookdb::WebhookResponse.ok
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def calculate_backfill_state_machine
|
|
94
|
+
step = Webhookdb::Replicator::StateMachineStep.new
|
|
95
|
+
# Must set this to fake out having credentials.
|
|
96
|
+
self.service_integration.update(backfill_key: "placeholder") if self.service_integration.backfill_key.blank?
|
|
97
|
+
if self.service_integration.api_url.blank?
|
|
98
|
+
step.output = %(You're about to sync entries from an Atom URL into WebhookDB.
|
|
99
|
+
This will create a row for each 'entry' in the given feed,
|
|
100
|
+
and insert/update new rows periodically.
|
|
101
|
+
|
|
102
|
+
Paste in the URL to sync, and press Enter.)
|
|
103
|
+
return step.prompting("URL").api_url(self.service_integration)
|
|
104
|
+
end
|
|
105
|
+
unless (result = self.verify_backfill_credentials).verified
|
|
106
|
+
self.service_integration.update(api_url: "")
|
|
107
|
+
step.output = result.message
|
|
108
|
+
return step.prompting("URL").api_url(self.service_integration)
|
|
109
|
+
end
|
|
110
|
+
step.output = %(
|
|
111
|
+
All set! Your feed will be synced momentarily and then every few hours after that.
|
|
112
|
+
|
|
113
|
+
#{self._query_help_output})
|
|
114
|
+
return step.completed
|
|
115
|
+
end
|
|
116
|
+
end
|