webhookdb 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/admin-dist/assets/index-6aebf805.js +264 -0
- data/admin-dist/favicon.ico +0 -0
- data/admin-dist/index.html +130 -0
- data/admin-dist/manifest.json +15 -0
- data/data/messages/replicators/url-recorder.liquid +20 -0
- data/data/messages/templates/errors/signalwire_send_sms.email.liquid +31 -0
- data/data/messages/web/install-customer-login.liquid +6 -5
- data/data/messages/web/install-error.liquid +1 -1
- data/data/messages/web/install-forbidden.liquid +25 -0
- data/data/messages/web/install-org-chooser.liquid +40 -0
- data/data/messages/web/install-success.liquid +2 -1
- data/data/messages/web/install.liquid +2 -1
- data/data/messages/web/partials/head.liquid +2 -0
- data/data/messages/web/styles.liquid +24 -0
- data/db/migrations/040_saved_query_fix_unique.rb +17 -0
- data/db/migrations/041_views.rb +20 -0
- data/db/migrations/042_sint_lock.rb +10 -0
- data/db/migrations/043_text_search.rb +28 -0
- data/db/migrations/044_oauth_session_token_cache.rb +21 -0
- data/integration/auth_spec.rb +2 -2
- data/lib/sequel/plugins/text_searchable.rb +165 -0
- data/lib/sequel/text_searchable.rb +42 -0
- data/lib/webhookdb/admin_api/auth.rb +24 -3
- data/lib/webhookdb/admin_api/data_provider.rb +196 -0
- data/lib/webhookdb/admin_api/entities.rb +143 -28
- data/lib/webhookdb/admin_api.rb +0 -2
- data/lib/webhookdb/api/auth.rb +5 -6
- data/lib/webhookdb/api/db.rb +31 -6
- data/lib/webhookdb/api/entities.rb +7 -1
- data/lib/webhookdb/api/helpers.rb +6 -25
- data/lib/webhookdb/api/install.rb +204 -79
- data/lib/webhookdb/api/organizations.rb +14 -12
- data/lib/webhookdb/api/saved_queries.rb +10 -3
- data/lib/webhookdb/api/saved_views.rb +99 -0
- data/lib/webhookdb/api/service_integrations.rb +15 -9
- data/lib/webhookdb/api/subscriptions.rb +3 -1
- data/lib/webhookdb/api/sync_targets.rb +9 -7
- data/lib/webhookdb/api/system.rb +1 -0
- data/lib/webhookdb/api/webhook_subscriptions.rb +3 -1
- data/lib/webhookdb/apps.rb +30 -7
- data/lib/webhookdb/async/audit_logger.rb +2 -0
- data/lib/webhookdb/async.rb +5 -0
- data/lib/webhookdb/backfill_job/service_integration_lock.rb +22 -0
- data/lib/webhookdb/backfill_job.rb +9 -0
- data/lib/webhookdb/customer.rb +5 -0
- data/lib/webhookdb/database_document.rb +1 -1
- data/lib/webhookdb/db_adapter/default_sql.rb +1 -1
- data/lib/webhookdb/db_adapter.rb +20 -4
- data/lib/webhookdb/fixtures/message_bodies.rb +34 -0
- data/lib/webhookdb/fixtures/organizations.rb +5 -0
- data/lib/webhookdb/fixtures/roles.rb +14 -0
- data/lib/webhookdb/fixtures/saved_views.rb +25 -0
- data/lib/webhookdb/fixtures/webhook_subscription_deliveries.rb +18 -0
- data/lib/webhookdb/http.rb +8 -2
- data/lib/webhookdb/icalendar.rb +3 -0
- data/lib/webhookdb/idempotency.rb +69 -22
- data/lib/webhookdb/increase.rb +69 -21
- data/lib/webhookdb/intercom.rb +10 -3
- data/lib/webhookdb/jobs/backfill.rb +3 -1
- data/lib/webhookdb/jobs/emailer.rb +0 -1
- data/lib/webhookdb/jobs/icalendar_delete_stale_cancelled_events.rb +19 -0
- data/lib/webhookdb/jobs/icalendar_enqueue_syncs.rb +1 -1
- data/lib/webhookdb/jobs/icalendar_sync.rb +1 -1
- data/lib/webhookdb/jobs/increase_event_handler.rb +20 -0
- data/lib/webhookdb/jobs/scheduled_backfills.rb +2 -1
- data/lib/webhookdb/jobs/sync_target_run_sync.rb +3 -1
- data/lib/webhookdb/message/body.rb +6 -4
- data/lib/webhookdb/message/delivery.rb +2 -0
- data/lib/webhookdb/messages/error_icalendar_fetch.rb +1 -2
- data/lib/webhookdb/messages/error_signalwire_send_sms.rb +48 -0
- data/lib/webhookdb/oauth/fake_provider.rb +44 -0
- data/lib/webhookdb/oauth/front_provider.rb +1 -2
- data/lib/webhookdb/oauth/increase_provider.rb +80 -0
- data/lib/webhookdb/oauth/intercom_provider.rb +3 -11
- data/lib/webhookdb/oauth/session.rb +20 -0
- data/lib/webhookdb/oauth.rb +7 -21
- data/lib/webhookdb/organization/alerting.rb +2 -0
- data/lib/webhookdb/organization/database_migration.rb +3 -0
- data/lib/webhookdb/organization.rb +37 -6
- data/lib/webhookdb/organization_membership.rb +14 -7
- data/lib/webhookdb/postgres.rb +2 -0
- data/lib/webhookdb/replicator/base.rb +1 -0
- data/lib/webhookdb/replicator/docgen.rb +9 -1
- data/lib/webhookdb/replicator/fake.rb +2 -3
- data/lib/webhookdb/replicator/front_signalwire_message_channel_app_v1.rb +49 -14
- data/lib/webhookdb/replicator/icalendar_calendar_v1.rb +97 -17
- data/lib/webhookdb/replicator/icalendar_event_v1.rb +104 -2
- data/lib/webhookdb/replicator/increase_account_number_v1.rb +6 -43
- data/lib/webhookdb/replicator/increase_account_transfer_v1.rb +7 -24
- data/lib/webhookdb/replicator/increase_account_v1.rb +7 -31
- data/lib/webhookdb/replicator/increase_ach_transfer_v1.rb +5 -43
- data/lib/webhookdb/replicator/increase_app_v1.rb +78 -0
- data/lib/webhookdb/replicator/increase_check_transfer_v1.rb +23 -29
- data/lib/webhookdb/replicator/increase_event_v1.rb +41 -0
- data/lib/webhookdb/replicator/increase_limit_v1.rb +9 -34
- data/lib/webhookdb/replicator/increase_transaction_v1.rb +5 -30
- data/lib/webhookdb/replicator/increase_v1_mixin.rb +58 -78
- data/lib/webhookdb/replicator/increase_wire_transfer_v1.rb +5 -24
- data/lib/webhookdb/replicator/intercom_contact_v1.rb +51 -4
- data/lib/webhookdb/replicator/intercom_conversation_v1.rb +42 -6
- data/lib/webhookdb/replicator/intercom_marketplace_root_v1.rb +2 -13
- data/lib/webhookdb/replicator/intercom_v1_mixin.rb +20 -16
- data/lib/webhookdb/replicator/oauth_refresh_access_token_mixin.rb +1 -1
- data/lib/webhookdb/replicator/sponsy_v1_mixin.rb +1 -1
- data/lib/webhookdb/replicator/transistor_episode_v1.rb +17 -0
- data/lib/webhookdb/replicator/url_recorder_v1.rb +137 -0
- data/lib/webhookdb/replicator/webhook_request.rb +4 -0
- data/lib/webhookdb/replicator.rb +8 -0
- data/lib/webhookdb/role.rb +5 -2
- data/lib/webhookdb/saved_query.rb +23 -0
- data/lib/webhookdb/saved_view.rb +73 -0
- data/lib/webhookdb/sentry.rb +2 -0
- data/lib/webhookdb/service/entities.rb +0 -4
- data/lib/webhookdb/service/helpers.rb +5 -0
- data/lib/webhookdb/service/middleware.rb +17 -0
- data/lib/webhookdb/service/types.rb +10 -8
- data/lib/webhookdb/service/validators.rb +1 -2
- data/lib/webhookdb/service/view_api.rb +1 -1
- data/lib/webhookdb/service_integration.rb +17 -15
- data/lib/webhookdb/spec_helpers/shared_examples_for_replicators.rb +8 -8
- data/lib/webhookdb/spec_helpers/whdb.rb +3 -2
- data/lib/webhookdb/subscription.rb +2 -0
- data/lib/webhookdb/sync_target.rb +10 -2
- data/lib/webhookdb/tasks/message.rb +3 -1
- data/lib/webhookdb/version.rb +1 -1
- data/lib/webhookdb/webhook_subscription/delivery.rb +2 -0
- data/lib/webhookdb/webhook_subscription.rb +2 -0
- metadata +58 -9
- data/lib/webhookdb/admin_api/customers.rb +0 -63
- data/lib/webhookdb/admin_api/message_deliveries.rb +0 -61
- data/lib/webhookdb/admin_api/roles.rb +0 -15
@@ -10,13 +10,15 @@ require "webhookdb/async/audit_logger"
|
|
10
10
|
require "webhookdb/jobs/process_webhook"
|
11
11
|
|
12
12
|
class Webhookdb::API::ServiceIntegrations < Webhookdb::API::V1
|
13
|
+
include Webhookdb::Service::Types
|
14
|
+
|
13
15
|
# These URLs are not used by the CLI-
|
14
16
|
# they are the url that customers should point their webhooks to.
|
15
17
|
# We can't check org permissions on this endpoint
|
16
18
|
# because external services (so no auth) will be posting webhooks here.
|
17
19
|
# Depend on webhook verification to ensure the request is valid.
|
18
20
|
resource :service_integrations do
|
19
|
-
route [:post, :put, :delete, :patch], "/:opaque_id*" do
|
21
|
+
route [:get, :post, :put, :delete, :patch], "/:opaque_id*" do
|
20
22
|
opaque_id = params[:opaque_id]
|
21
23
|
handle_webhook_request(opaque_id) do
|
22
24
|
Webhookdb::ServiceIntegration[opaque_id:] or merror!(400, "No integration with that id")
|
@@ -41,7 +43,7 @@ class Webhookdb::API::ServiceIntegrations < Webhookdb::API::V1
|
|
41
43
|
resource :create do
|
42
44
|
helpers do
|
43
45
|
def create_integration(org, name)
|
44
|
-
available_services_list = org.
|
46
|
+
available_services_list = org.available_replicators.map(&:name).sort.join("\n\t")
|
45
47
|
|
46
48
|
service_name_invalid = Webhookdb::Replicator.registered(name).nil?
|
47
49
|
if service_name_invalid
|
@@ -55,7 +57,7 @@ Run `webhookdb services list` to see available services, and try again with the
|
|
55
57
|
end
|
56
58
|
|
57
59
|
# If org does not have access to the given service
|
58
|
-
unless org.
|
60
|
+
unless org.available_replicators.map(&:name).include?(name)
|
59
61
|
step = Webhookdb::Replicator::StateMachineStep.new
|
60
62
|
step.needs_input = false
|
61
63
|
step.output =
|
@@ -95,13 +97,13 @@ If the list does not look correct, you can contact support at #{Webhookdb.suppor
|
|
95
97
|
:guard_confirm,
|
96
98
|
"WARNING: #{org.name} already has an integration for service #{params[:service_name]}.\n" \
|
97
99
|
"Press Enter to create another, or Ctrl+C to quit.\n" \
|
98
|
-
"Modify the existing integration using `webhookdb integrations #{existing.opaque_id}
|
100
|
+
"Modify the existing integration using `webhookdb integrations setup #{existing.opaque_id}`",
|
99
101
|
)
|
100
102
|
end
|
101
103
|
end
|
102
104
|
desc "Create service integration on a given organization"
|
103
105
|
params do
|
104
|
-
optional :service_name, type:
|
106
|
+
optional :service_name, type: TrimmedString,
|
105
107
|
prompt: "Enter the name of the service to create an integration for.\n" \
|
106
108
|
"Run 'webhookdb services list' to see available services:"
|
107
109
|
optional :guard_confirm
|
@@ -187,7 +189,9 @@ If the list does not look correct, you can contact support at #{Webhookdb.suppor
|
|
187
189
|
|
188
190
|
desc "Returns information about the integration."
|
189
191
|
params do
|
190
|
-
optional :field,
|
192
|
+
optional :field,
|
193
|
+
type: TrimmedString,
|
194
|
+
values: TrimmedString.map(Webhookdb::ServiceIntegration::INTEGRATION_INFO_FIELDS.keys + [""])
|
191
195
|
end
|
192
196
|
post :info do
|
193
197
|
ensure_plan_supports!
|
@@ -339,7 +343,7 @@ If the list does not look correct, you can contact support at #{Webhookdb.suppor
|
|
339
343
|
end
|
340
344
|
|
341
345
|
params do
|
342
|
-
optional :confirm, type:
|
346
|
+
optional :confirm, type: TrimmedString
|
343
347
|
end
|
344
348
|
post :delete do
|
345
349
|
ensure_plan_supports!
|
@@ -384,9 +388,11 @@ The tables and all data for this integration and its dependents will also be rem
|
|
384
388
|
|
385
389
|
params do
|
386
390
|
optional :new_name,
|
387
|
-
type:
|
391
|
+
type: TrimmedString,
|
388
392
|
db_identifier: true,
|
389
|
-
prompt: "Enter the new name of the table. " +
|
393
|
+
prompt: "Enter the new name of the table. " +
|
394
|
+
Webhookdb::DBAdapter::INVALID_IDENTIFIER_PROMPT +
|
395
|
+
"\nTable name:"
|
390
396
|
end
|
391
397
|
post :rename_table do
|
392
398
|
org = lookup_org!
|
@@ -6,6 +6,8 @@ require "webhookdb/api"
|
|
6
6
|
require "webhookdb/admin_api"
|
7
7
|
|
8
8
|
class Webhookdb::API::Subscriptions < Webhookdb::API::V1
|
9
|
+
include Webhookdb::Service::Types
|
10
|
+
|
9
11
|
resource :organizations do
|
10
12
|
route_param :org_identifier, type: String do
|
11
13
|
resource :subscriptions do
|
@@ -19,7 +21,7 @@ class Webhookdb::API::Subscriptions < Webhookdb::API::V1
|
|
19
21
|
|
20
22
|
desc "Authenticates stripe user and returns stripe checkout session or billing portal url"
|
21
23
|
params do
|
22
|
-
optional :plan, type:
|
24
|
+
optional :plan, type: TrimmedString
|
23
25
|
optional :guard_confirm
|
24
26
|
end
|
25
27
|
post :open_portal do
|
@@ -5,6 +5,8 @@ require "webhookdb/jobs/sync_target_run_sync"
|
|
5
5
|
|
6
6
|
# rubocop:disable Layout/LineLength
|
7
7
|
class Webhookdb::API::SyncTargets < Webhookdb::API::V1
|
8
|
+
include Webhookdb::Service::Types
|
9
|
+
|
8
10
|
class ConnectionUrlType < Grape::Validations::Validators::Base
|
9
11
|
def validate!(params)
|
10
12
|
url = params[:connection_url]
|
@@ -46,14 +48,14 @@ class Webhookdb::API::SyncTargets < Webhookdb::API::V1
|
|
46
48
|
disable: ->(req) { req.path.end_with?("/update") },
|
47
49
|
}
|
48
50
|
is_db && optional(:schema,
|
49
|
-
type:
|
51
|
+
type: TrimmedString,
|
50
52
|
db_identifier: true,
|
51
53
|
allow_blank: true,
|
52
54
|
desc: "Schema (or namespace) to write the table into. Default to no schema/namespace.",)
|
53
55
|
# The description here says there is a default value, but the default value isn't actually saved to the SyncTarget
|
54
56
|
# object--it's inferred in the SyncTarget sync behavior when the table value is a blank string.
|
55
57
|
is_db && optional(:table,
|
56
|
-
type:
|
58
|
+
type: TrimmedString,
|
57
59
|
db_identifier: true,
|
58
60
|
allow_blank: true,
|
59
61
|
desc: "Table to create and update. Default to match the table name of the service integration.",)
|
@@ -64,7 +66,7 @@ class Webhookdb::API::SyncTargets < Webhookdb::API::V1
|
|
64
66
|
end
|
65
67
|
params :connection_url do
|
66
68
|
optional :connection_url,
|
67
|
-
type:
|
69
|
+
type: TrimmedString,
|
68
70
|
prompt: "Enter the #{is_db ? 'database connection string' : 'HTTP endpoint'} that WebhookDB should sync data to:",
|
69
71
|
connection_url_type: is_db ? "db" : "http"
|
70
72
|
end
|
@@ -112,7 +114,7 @@ class Webhookdb::API::SyncTargets < Webhookdb::API::V1
|
|
112
114
|
params do
|
113
115
|
use :connection_url
|
114
116
|
use :sync_target_params
|
115
|
-
requires :service_integration_identifier, type:
|
117
|
+
requires :service_integration_identifier, type: TrimmedString, allow_blank: false
|
116
118
|
end
|
117
119
|
route_setting :target_type, target_type_resource
|
118
120
|
post :create do
|
@@ -153,8 +155,8 @@ class Webhookdb::API::SyncTargets < Webhookdb::API::V1
|
|
153
155
|
end
|
154
156
|
end
|
155
157
|
params do
|
156
|
-
optional :user, type:
|
157
|
-
optional :password, type:
|
158
|
+
optional :user, type: TrimmedString, prompt: "Username for the connection:"
|
159
|
+
optional :password, type: TrimmedString, prompt: "Password for the connection:"
|
158
160
|
end
|
159
161
|
route_setting :target_type, target_type_resource
|
160
162
|
post :update_credentials do
|
@@ -187,7 +189,7 @@ class Webhookdb::API::SyncTargets < Webhookdb::API::V1
|
|
187
189
|
end
|
188
190
|
|
189
191
|
params do
|
190
|
-
optional :confirm, type:
|
192
|
+
optional :confirm, type: TrimmedString
|
191
193
|
end
|
192
194
|
route_setting :target_type, target_type_resource
|
193
195
|
post :delete do
|
data/lib/webhookdb/api/system.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require "webhookdb/api"
|
4
4
|
|
5
5
|
class Webhookdb::API::WebhookSubscriptions < Webhookdb::API::V1
|
6
|
+
include Webhookdb::Service::Types
|
7
|
+
|
6
8
|
resource :organizations do
|
7
9
|
route_param :org_identifier, type: String do
|
8
10
|
resource :webhook_subscriptions do
|
@@ -21,7 +23,7 @@ class Webhookdb::API::WebhookSubscriptions < Webhookdb::API::V1
|
|
21
23
|
|
22
24
|
params do
|
23
25
|
optional :service_integration_identifier,
|
24
|
-
type:
|
26
|
+
type: TrimmedString,
|
25
27
|
desc: "If provided, attach the webhook subscription to this integration rather than the org.",
|
26
28
|
prompt: "Which integration is this for? Use the service name, table name, or opaque id.\n" \
|
27
29
|
"See your integrations with `webhookdb integrations list`:"
|
data/lib/webhookdb/apps.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require "grape"
|
4
4
|
require "grape-swagger"
|
5
|
+
require "rack/dynamic_config_writer"
|
6
|
+
require "rack/spa_app"
|
5
7
|
require "sidekiq/web"
|
6
8
|
require "sidekiq/cron/web"
|
7
9
|
|
@@ -18,6 +20,7 @@ require "webhookdb/api/me"
|
|
18
20
|
require "webhookdb/api/organizations"
|
19
21
|
require "webhookdb/api/replay"
|
20
22
|
require "webhookdb/api/saved_queries"
|
23
|
+
require "webhookdb/api/saved_views"
|
21
24
|
require "webhookdb/api/service_integrations"
|
22
25
|
require "webhookdb/api/services"
|
23
26
|
require "webhookdb/api/stripe"
|
@@ -27,14 +30,16 @@ require "webhookdb/api/system"
|
|
27
30
|
require "webhookdb/api/webhook_subscriptions"
|
28
31
|
|
29
32
|
require "webhookdb/admin_api/auth"
|
30
|
-
require "webhookdb/admin_api/customers"
|
31
33
|
require "webhookdb/admin_api/database_documents"
|
32
|
-
require "webhookdb/admin_api/
|
33
|
-
require "webhookdb/admin_api/roles"
|
34
|
+
require "webhookdb/admin_api/data_provider"
|
34
35
|
|
35
36
|
require "webterm/apps"
|
36
37
|
|
37
38
|
module Webhookdb::Apps
|
39
|
+
REDIRECTS = {
|
40
|
+
"/increase" => "/v1/install/increase",
|
41
|
+
}.freeze
|
42
|
+
|
38
43
|
# Call this from your rackup file, like config.ru.
|
39
44
|
#
|
40
45
|
# @example
|
@@ -48,15 +53,22 @@ module Webhookdb::Apps
|
|
48
53
|
def self.rack_up(config_ru)
|
49
54
|
Webhookdb::Async.setup_web
|
50
55
|
config_ru.instance_exec do
|
51
|
-
map "/
|
56
|
+
map "/admin_api" do
|
52
57
|
run Webhookdb::Apps::AdminAPI.build_app
|
53
58
|
end
|
59
|
+
map "/admin" do
|
60
|
+
run Webhookdb::Apps::Admin.to_app
|
61
|
+
end
|
54
62
|
map "/sidekiq" do
|
55
63
|
run Webhookdb::Apps::SidekiqWeb.to_app
|
56
64
|
end
|
57
65
|
map "/terminal" do
|
58
66
|
run Webhookdb::Apps::Webterm.to_app
|
59
67
|
end
|
68
|
+
use Rack::SimpleRedirect, routes: (REDIRECTS.each_with_object({}) do |(k, v), memo|
|
69
|
+
memo[k] = v
|
70
|
+
memo["#{k}/"] = v
|
71
|
+
end)
|
60
72
|
run Webhookdb::Apps::API.build_app
|
61
73
|
end
|
62
74
|
end
|
@@ -70,6 +82,7 @@ module Webhookdb::Apps
|
|
70
82
|
mount Webhookdb::API::Organizations
|
71
83
|
mount Webhookdb::API::Replay
|
72
84
|
mount Webhookdb::API::SavedQueries
|
85
|
+
mount Webhookdb::API::SavedViews
|
73
86
|
mount Webhookdb::API::ServiceIntegrations
|
74
87
|
mount Webhookdb::API::Services
|
75
88
|
mount Webhookdb::API::Stripe
|
@@ -83,12 +96,22 @@ module Webhookdb::Apps
|
|
83
96
|
class AdminAPI < Webhookdb::Service
|
84
97
|
mount Webhookdb::AdminAPI::Auth
|
85
98
|
mount Webhookdb::AdminAPI::DatabaseDocuments
|
86
|
-
mount Webhookdb::AdminAPI::
|
87
|
-
mount Webhookdb::AdminAPI::Roles
|
88
|
-
mount Webhookdb::AdminAPI::Customers
|
99
|
+
mount Webhookdb::AdminAPI::DataProvider
|
89
100
|
add_swagger_documentation if ENV["RACK_ENV"] == "development"
|
90
101
|
end
|
91
102
|
|
103
|
+
Admin = Rack::Builder.new do
|
104
|
+
build_dir = Pathname(__FILE__).dirname.parent.parent + "admin-dist"
|
105
|
+
dw = Rack::DynamicConfigWriter.new(build_dir + "index.html", global_assign: "window.whdbDynamicEnv")
|
106
|
+
env = {
|
107
|
+
"VITE_API_ROOT" => "/",
|
108
|
+
"VITE_RELEASE" => "admin@1.0.0",
|
109
|
+
"NODE_ENV" => "production",
|
110
|
+
}.merge(Rack::DynamicConfigWriter.pick_env("VITE_"))
|
111
|
+
index_bytes = dw.as_string(env)
|
112
|
+
Rack::SpaApp.run_spa_app(self, build_dir, enforce_ssl: Webhookdb::Service.enforce_ssl, index_bytes:)
|
113
|
+
end
|
114
|
+
|
92
115
|
SidekiqWeb = Rack::Builder.new do
|
93
116
|
use Sentry::Rack::CaptureExceptions if Webhookdb::Sentry.enabled?
|
94
117
|
use Rack::Auth::Basic, "Protected Area" do |username, password|
|
@@ -11,6 +11,8 @@ class Webhookdb::Async::AuditLogger < Amigo::AuditLogger
|
|
11
11
|
MAX_STR_LEN = 64
|
12
12
|
STR_PREFIX_LEN = 12
|
13
13
|
|
14
|
+
def audit_log_level = Webhookdb::Async.audit_log_level
|
15
|
+
|
14
16
|
def perform(event_json)
|
15
17
|
j2 = event_json.dup
|
16
18
|
j2["payload"] = self.trim_long_strings(j2["payload"], max_str_len: MAX_STR_LEN, str_prefix_len: STR_PREFIX_LEN)
|
data/lib/webhookdb/async.rb
CHANGED
@@ -27,6 +27,11 @@ module Webhookdb::Async
|
|
27
27
|
# at `warn` level.
|
28
28
|
setting :slow_job_seconds, 1.0
|
29
29
|
|
30
|
+
# The log level that Webhookdb::Async::AuditLogger logs at.
|
31
|
+
# By default, use :info, but :debug may be appropriate for higher-activity servers
|
32
|
+
# to reduce logging costs (the messages can be big).
|
33
|
+
setting :audit_log_level, :info
|
34
|
+
|
30
35
|
setting :sidekiq_redis_url, "redis://localhost:6379/0", key: "REDIS_URL"
|
31
36
|
setting :sidekiq_redis_provider, ""
|
32
37
|
# For sidekiq web UI. Randomize a default so they will only be useful if set.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Helper table so backfill jobs can take exclusive locks on a service integration.
|
4
|
+
# Otherwise we end up backfilling the same integration concurrently.
|
5
|
+
class Webhookdb::BackfillJob::ServiceIntegrationLock < Webhookdb::Postgres::Model(
|
6
|
+
:backfill_job_service_integration_locks,
|
7
|
+
)
|
8
|
+
|
9
|
+
many_to_one :service_integration, class: "Webhookdb::ServiceIntegration"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Table: backfill_job_service_integration_locks
|
13
|
+
# -------------------------------------------------------------------------------------------------------------------------------------------------
|
14
|
+
# Columns:
|
15
|
+
# id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
|
16
|
+
# service_integration_id | integer | NOT NULL
|
17
|
+
# Indexes:
|
18
|
+
# backfill_job_service_integration_locks_pkey | PRIMARY KEY btree (id)
|
19
|
+
# backfill_job_service_integration_loc_service_integration_id_key | UNIQUE btree (service_integration_id)
|
20
|
+
# Foreign key constraints:
|
21
|
+
# backfill_job_service_integration_lo_service_integration_id_fkey | (service_integration_id) REFERENCES service_integrations(id) ON DELETE CASCADE
|
22
|
+
# -------------------------------------------------------------------------------------------------------------------------------------------------
|
@@ -15,6 +15,7 @@
|
|
15
15
|
#
|
16
16
|
class Webhookdb::BackfillJob < Webhookdb::Postgres::Model(:backfill_jobs)
|
17
17
|
plugin :timestamps
|
18
|
+
plugin :text_searchable, terms: [:service_integration]
|
18
19
|
|
19
20
|
many_to_one :service_integration, class: "Webhookdb::ServiceIntegration"
|
20
21
|
many_to_one :parent_job, class: "Webhookdb::BackfillJob"
|
@@ -71,6 +72,12 @@ class Webhookdb::BackfillJob < Webhookdb::Postgres::Model(:backfill_jobs)
|
|
71
72
|
self.child_jobs.each(&:enqueue)
|
72
73
|
end
|
73
74
|
|
75
|
+
def ensure_service_integration_lock
|
76
|
+
return Webhookdb::BackfillJob::ServiceIntegrationLock.find_or_create_or_find(
|
77
|
+
service_integration_id: self.service_integration_id,
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
74
81
|
#
|
75
82
|
# :section: Sequel Hooks
|
76
83
|
#
|
@@ -93,6 +100,8 @@ end
|
|
93
100
|
# parent_job_id | integer |
|
94
101
|
# created_by_id | integer |
|
95
102
|
# incremental | boolean | NOT NULL
|
103
|
+
# criteria | jsonb | NOT NULL DEFAULT '{}'::jsonb
|
104
|
+
# text_search | tsvector |
|
96
105
|
# Indexes:
|
97
106
|
# backfill_jobs_pkey | PRIMARY KEY btree (id)
|
98
107
|
# backfill_jobs_opaque_id_key | UNIQUE btree (opaque_id)
|
data/lib/webhookdb/customer.rb
CHANGED
@@ -37,6 +37,7 @@ class Webhookdb::Customer < Webhookdb::Postgres::Model(:customers)
|
|
37
37
|
|
38
38
|
plugin :timestamps
|
39
39
|
plugin :soft_deletes
|
40
|
+
plugin :text_searchable, terms: [:name, :email]
|
40
41
|
|
41
42
|
one_to_many :all_memberships, class: "Webhookdb::OrganizationMembership"
|
42
43
|
one_to_many :invited_memberships,
|
@@ -329,6 +330,7 @@ end
|
|
329
330
|
# name | text | NOT NULL DEFAULT ''::text
|
330
331
|
# note | text | NOT NULL DEFAULT ''::text
|
331
332
|
# opaque_id | text | NOT NULL
|
333
|
+
# text_search | tsvector |
|
332
334
|
# Indexes:
|
333
335
|
# customers_pkey | PRIMARY KEY btree (id)
|
334
336
|
# customers_email_key | UNIQUE btree (email)
|
@@ -339,9 +341,12 @@ end
|
|
339
341
|
# backfill_jobs | backfill_jobs_created_by_id_fkey | (created_by_id) REFERENCES customers(id) ON DELETE SET NULL
|
340
342
|
# customer_reset_codes | customer_reset_codes_customer_id_fkey | (customer_id) REFERENCES customers(id) ON DELETE CASCADE
|
341
343
|
# message_deliveries | message_deliveries_recipient_id_fkey | (recipient_id) REFERENCES customers(id) ON DELETE SET NULL
|
344
|
+
# oauth_sessions | oauth_sessions_customer_id_fkey | (customer_id) REFERENCES customers(id) ON DELETE CASCADE
|
342
345
|
# organization_database_migrations | organization_database_migrations_started_by_id_fkey | (started_by_id) REFERENCES customers(id) ON DELETE SET NULL
|
343
346
|
# organization_memberships | organization_memberships_customer_id_fkey | (customer_id) REFERENCES customers(id)
|
344
347
|
# roles_customers | roles_customers_customer_id_fkey | (customer_id) REFERENCES customers(id)
|
348
|
+
# saved_queries | saved_queries_created_by_id_fkey | (created_by_id) REFERENCES customers(id) ON DELETE SET NULL
|
349
|
+
# saved_views | saved_views_created_by_id_fkey | (created_by_id) REFERENCES customers(id) ON DELETE SET NULL
|
345
350
|
# sync_targets | sync_targets_created_by_id_fkey | (created_by_id) REFERENCES customers(id) ON DELETE SET NULL
|
346
351
|
# webhook_subscriptions | webhook_subscriptions_created_by_id_fkey | (created_by_id) REFERENCES customers(id)
|
347
352
|
# -----------------------------------------------------------------------------------------------------------------------------------------------------
|
@@ -52,7 +52,7 @@ class Webhookdb::DatabaseDocument < Webhookdb::Postgres::Model(:database_documen
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def presigned_view_url(expire_at:, **kw)
|
55
|
-
url = "#{Webhookdb.api_url}/
|
55
|
+
url = "#{Webhookdb.api_url}/admin_api/v1/database_documents/#{self.id}/view"
|
56
56
|
return self.sign_url(url, expire_at:, **kw)
|
57
57
|
end
|
58
58
|
end
|
@@ -28,7 +28,7 @@ module Webhookdb::DBAdapter::DefaultSql
|
|
28
28
|
def escape_identifier(s)
|
29
29
|
s = s.to_s
|
30
30
|
raise ArgumentError, "#{s} is an invalid identifier and should have been validated previously" unless
|
31
|
-
Webhookdb::DBAdapter
|
31
|
+
Webhookdb::DBAdapter.valid_identifier?(s)
|
32
32
|
|
33
33
|
quo = self.identifier_quote_char
|
34
34
|
return "#{quo}#{s}#{quo}" if RESERVED_KEYWORDS.include?(s.upcase) ||
|
data/lib/webhookdb/db_adapter.rb
CHANGED
@@ -6,10 +6,14 @@ class Webhookdb::DBAdapter
|
|
6
6
|
class UnsupportedAdapter < StandardError; end
|
7
7
|
|
8
8
|
VALID_IDENTIFIER = /^[a-zA-Z][a-zA-Z\d_ ]*$/
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
INVALID_IDENTIFIER_PROMPT =
|
10
|
+
"Identifiers must start with a letter and contain only letters, numbers, spaces, and underscores.\n" \
|
11
|
+
"See https://docs.webhookdb.com/concepts/valid-identifiers/ for rules\n" \
|
12
|
+
"about identifiers like schema, table, and column names."
|
13
|
+
|
14
|
+
INVALID_IDENTIFIER_MESSAGE = INVALID_IDENTIFIER_PROMPT.tr("\n", " ")
|
15
|
+
|
16
|
+
class InvalidIdentifier < Webhookdb::InvalidInput; end
|
13
17
|
|
14
18
|
class Schema < Webhookdb::TypedStruct
|
15
19
|
attr_reader :name
|
@@ -202,6 +206,18 @@ class Webhookdb::DBAdapter
|
|
202
206
|
def self.supported_adapters_message
|
203
207
|
return "Postgres (postgres://), SnowflakeDB (snowflake://)"
|
204
208
|
end
|
209
|
+
|
210
|
+
def self.valid_identifier?(s) = VALID_IDENTIFIER.match?(s)
|
211
|
+
|
212
|
+
# Raise if the identifier +s+ is invalid according to +VALID_IDENTIFIER+.
|
213
|
+
# +type+ is used in the error message, like 'Sorry, this is not a valid table name.'
|
214
|
+
# If the user tries SQL injection, let them know we noticed!
|
215
|
+
def self.validate_identifier!(s, type:)
|
216
|
+
return if self.valid_identifier?(s)
|
217
|
+
msg = "Sorry, this is not a valid #{type} name. #{INVALID_IDENTIFIER_MESSAGE}"
|
218
|
+
msg += " And we see you what you did there ;)" if s.include?(";") && s.downcase.include?("drop")
|
219
|
+
raise InvalidIdentifier, msg
|
220
|
+
end
|
205
221
|
end
|
206
222
|
|
207
223
|
require "webhookdb/db_adapter/pg"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "webhookdb/fixtures"
|
4
|
+
|
5
|
+
module Webhookdb::Fixtures::MessageBody
|
6
|
+
extend Webhookdb::Fixtures
|
7
|
+
|
8
|
+
fixtured_class Webhookdb::Message::Body
|
9
|
+
|
10
|
+
base :message_body do
|
11
|
+
self.mediatype ||= "text/plain"
|
12
|
+
self.content ||= Faker::Lorem.paragraph
|
13
|
+
end
|
14
|
+
|
15
|
+
before_saving do |instance|
|
16
|
+
instance.delivery ||= Webhookdb::Fixtures.message_delivery.create
|
17
|
+
instance
|
18
|
+
end
|
19
|
+
|
20
|
+
decorator :html do
|
21
|
+
self.mediatype = "text/html"
|
22
|
+
self.content = "<html><body><p>#{Faker::Lorem.sentence}</p></body></html>"
|
23
|
+
end
|
24
|
+
|
25
|
+
decorator :text do
|
26
|
+
self.mediatype = "text/plain"
|
27
|
+
self.content = Faker::Lorem.paragraph
|
28
|
+
end
|
29
|
+
|
30
|
+
decorator :subject do
|
31
|
+
self.mediatype = "subject"
|
32
|
+
self.content = Faker::Lorem.sentence
|
33
|
+
end
|
34
|
+
end
|
@@ -20,6 +20,11 @@ module Webhookdb::Fixtures::Organizations
|
|
20
20
|
Webhookdb::Fixtures.organization_membership.verified.create(customer: c, organization: self)
|
21
21
|
end
|
22
22
|
|
23
|
+
decorator :with_admin, presave: true do |c={}|
|
24
|
+
c = Webhookdb::Fixtures.customer.create(c) unless c.is_a?(Webhookdb::Customer)
|
25
|
+
Webhookdb::Fixtures.organization_membership.verified.admin.create(customer: c, organization: self)
|
26
|
+
end
|
27
|
+
|
23
28
|
decorator :with_invite, presave: true do |c={}|
|
24
29
|
c = Webhookdb::Fixtures.customer.create(c) unless c.is_a?(Webhookdb::Customer)
|
25
30
|
Webhookdb::Fixtures.organization_membership.invite.create(customer: c, organization: self)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "webhookdb"
|
4
|
+
require "webhookdb/fixtures"
|
5
|
+
|
6
|
+
module Webhookdb::Fixtures::Roles
|
7
|
+
extend Webhookdb::Fixtures
|
8
|
+
|
9
|
+
fixtured_class Webhookdb::Role
|
10
|
+
|
11
|
+
base :role do
|
12
|
+
self.name ||= Faker::Lorem.word + SecureRandom.hex(2)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "webhookdb"
|
4
|
+
require "webhookdb/fixtures"
|
5
|
+
|
6
|
+
module Webhookdb::Fixtures::SavedViews
|
7
|
+
extend Webhookdb::Fixtures
|
8
|
+
|
9
|
+
fixtured_class Webhookdb::SavedView
|
10
|
+
|
11
|
+
base :saved_view do
|
12
|
+
self.name ||= "testview_#{SecureRandom.hex(3)}"
|
13
|
+
self.sql ||= "SELECT 'fixtured' AS testcol"
|
14
|
+
end
|
15
|
+
|
16
|
+
before_saving do |instance|
|
17
|
+
instance.organization ||= Webhookdb::Fixtures.organization.create
|
18
|
+
instance
|
19
|
+
end
|
20
|
+
|
21
|
+
decorator :created_by do |c={}|
|
22
|
+
c = Webhookdb::Fixtures.customer.create(c) unless c.is_a?(Webhookdb::Customer)
|
23
|
+
self.created_by = c
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "webhookdb/fixtures"
|
4
|
+
|
5
|
+
module Webhookdb::Fixtures::WebhookSubscriptionDeliveries
|
6
|
+
extend Webhookdb::Fixtures
|
7
|
+
|
8
|
+
fixtured_class Webhookdb::WebhookSubscription::Delivery
|
9
|
+
|
10
|
+
base :webhook_subscription_delivery do
|
11
|
+
self.payload ||= {}
|
12
|
+
end
|
13
|
+
|
14
|
+
before_saving do |instance|
|
15
|
+
instance.webhook_subscription ||= Webhookdb::Fixtures.webhook_subscription.create
|
16
|
+
instance
|
17
|
+
end
|
18
|
+
end
|
data/lib/webhookdb/http.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
4
|
-
|
3
|
+
require "appydays/configurable"
|
5
4
|
require "appydays/loggable/httparty_formatter"
|
5
|
+
require "httparty"
|
6
6
|
|
7
7
|
module Webhookdb::Http
|
8
|
+
include Appydays::Configurable
|
9
|
+
configurable(:http) do
|
10
|
+
setting :log_level, :debug
|
11
|
+
end
|
12
|
+
|
8
13
|
# Error raised when some API has rate limited us.
|
9
14
|
class BaseError < StandardError; end
|
10
15
|
|
@@ -88,6 +93,7 @@ module Webhookdb::Http
|
|
88
93
|
|
89
94
|
raise ArgumentError, "must pass :logger keyword" unless options.key?(:logger)
|
90
95
|
options[:log_format] = :appydays
|
96
|
+
options[:log_level] = self.log_level
|
91
97
|
end
|
92
98
|
|
93
99
|
# Convenience wrapper around Down that handles gzip.
|
data/lib/webhookdb/icalendar.rb
CHANGED
@@ -19,5 +19,8 @@ module Webhookdb::Icalendar
|
|
19
19
|
# or two threads for 7 hours. The resyncs are spread out across the sync period
|
20
20
|
# (ie, no thundering herd every 8 hours), but it is still a good idea to sync as infrequently as possible.
|
21
21
|
setting :sync_period_hours, 6
|
22
|
+
|
23
|
+
# Cancelled events that were cancelled this long ago are deleted from the database.
|
24
|
+
setting :stale_cancelled_event_threshold_days, 35
|
22
25
|
end
|
23
26
|
end
|