webhookdb 1.2.2 → 1.3.1
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/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 +9 -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 +9 -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 +57 -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
@@ -65,7 +65,7 @@ class Webhookdb::Replicator::IcalendarEventV1 < Webhookdb::Replicator::Base
|
|
65
65
|
ruby: lambda do |entry, **|
|
66
66
|
self.entry_to_date(entry) if entry.is_a?(Hash) && self.entry_is_date_str?(entry)
|
67
67
|
end,
|
68
|
-
sql:
|
68
|
+
sql: Webhookdb::Replicator::Column::NOT_IMPLEMENTED,
|
69
69
|
)
|
70
70
|
CONV_DATETIME = Webhookdb::Replicator::Column::IsomorphicProc.new(
|
71
71
|
ruby: lambda do |entry, **|
|
@@ -108,6 +108,8 @@ class Webhookdb::Replicator::IcalendarEventV1 < Webhookdb::Replicator::Base
|
|
108
108
|
sql: ->(_) { raise NotImplementedError },
|
109
109
|
)
|
110
110
|
|
111
|
+
def _webhook_response(_request) = Webhookdb::WebhookResponse.ok
|
112
|
+
|
111
113
|
def _remote_key_column
|
112
114
|
return Webhookdb::Replicator::Column.new(
|
113
115
|
:compound_identity,
|
@@ -126,7 +128,7 @@ class Webhookdb::Replicator::IcalendarEventV1 < Webhookdb::Replicator::Base
|
|
126
128
|
return [
|
127
129
|
col.new(:calendar_external_id, TEXT, index: true),
|
128
130
|
col.new(:uid, TEXT, data_key: ["UID", "v"], index: true),
|
129
|
-
col.new(:row_updated_at, TIMESTAMP, index: true
|
131
|
+
col.new(:row_updated_at, TIMESTAMP, index: true),
|
130
132
|
col.new(:last_modified_at,
|
131
133
|
TIMESTAMP,
|
132
134
|
index: true,
|
@@ -153,6 +155,8 @@ class Webhookdb::Replicator::IcalendarEventV1 < Webhookdb::Replicator::Base
|
|
153
155
|
]
|
154
156
|
end
|
155
157
|
|
158
|
+
def _timestamp_column_name = :last_modified_at
|
159
|
+
|
156
160
|
def _resource_and_event(request)
|
157
161
|
return request.body, nil
|
158
162
|
end
|
@@ -165,6 +169,41 @@ class Webhookdb::Replicator::IcalendarEventV1 < Webhookdb::Replicator::Base
|
|
165
169
|
return data
|
166
170
|
end
|
167
171
|
|
172
|
+
def _prepare_for_insert(resource, event, request, enrichment)
|
173
|
+
h = super
|
174
|
+
# Events can have a DTSTART, but no DTEND.
|
175
|
+
# https://icalendar.org/iCalendar-RFC-5545/3-6-1-event-component.html
|
176
|
+
# In these cases, we need to:
|
177
|
+
# - Use the duration, given.
|
178
|
+
# - Dates default to the next day.
|
179
|
+
# - Times default to start time.
|
180
|
+
if (_implicit_end_time = h[:start_at] && !h[:end_at])
|
181
|
+
self._set_implicit_end_at(resource, h)
|
182
|
+
elsif (_implicit_end_date = h[:start_date] && !h[:end_date])
|
183
|
+
self._set_implicit_end_date(resource, h)
|
184
|
+
end
|
185
|
+
return h
|
186
|
+
end
|
187
|
+
|
188
|
+
def _set_implicit_end_date(resource, h)
|
189
|
+
if (d = resource["DURATION"])
|
190
|
+
# See https://icalendar.org/iCalendar-RFC-5545/3-3-6-duration.html
|
191
|
+
dur = ActiveSupport::Duration.parse(d.fetch("v"))
|
192
|
+
h[:end_date] = h[:start_date] + dur
|
193
|
+
return
|
194
|
+
end
|
195
|
+
h[:end_date] = h[:start_date] + 1.day
|
196
|
+
end
|
197
|
+
|
198
|
+
def _set_implicit_end_at(resource, h)
|
199
|
+
if (d = resource["DURATION"])
|
200
|
+
dur = ActiveSupport::Duration.parse(d.fetch("v"))
|
201
|
+
h[:end_at] = h[:start_at] + dur
|
202
|
+
return
|
203
|
+
end
|
204
|
+
h[:end_at] = h[:start_at]
|
205
|
+
end
|
206
|
+
|
168
207
|
# @return [Array<Webhookdb::Replicator::IndexSpec>]
|
169
208
|
def _extra_index_specs
|
170
209
|
return [
|
@@ -220,6 +259,21 @@ class Webhookdb::Replicator::IcalendarEventV1 < Webhookdb::Replicator::Base
|
|
220
259
|
value.gsub!("\\r\\n", "\r\n")
|
221
260
|
value.gsub!("\\n", "\n")
|
222
261
|
value.gsub!("\\t", "\t")
|
262
|
+
# This line is not tested, since replicating issues with HTTP body encoding
|
263
|
+
# is really tricky (while I love Ruby's unicode handling, trying to replicate
|
264
|
+
# invalid data from other sources is a pain).
|
265
|
+
# However we do get invalid unicode sequences, like:
|
266
|
+
# DESCRIPTION:\r\nNFL regional registration opens 9/25 and ends 11/20\XAOwww.nflflag.com
|
267
|
+
# which cannot be encoded in JSON:
|
268
|
+
# Invalid Unicode [a0 77 77 77 2e] at 52 (JSON::GeneratorError)
|
269
|
+
# The only way I can think to handle this is with replacing invalid utf-8 chars
|
270
|
+
# (with the unicode questionmark icon), so they can be represented as JSON.
|
271
|
+
# This fix is here, and not in `Base#_to_json` (like the null char fixes),
|
272
|
+
# since I think this is an issue with feeds like icalendar,
|
273
|
+
# and not something to handle generally
|
274
|
+
# (we may also see this in something like Atom, but perhaps because
|
275
|
+
# atom is XML, not a 'plain' text format, it'll be more rare).
|
276
|
+
value.encode!("UTF-8", invalid: :replace, undef: :replace)
|
223
277
|
end
|
224
278
|
entry = {"v" => value}
|
225
279
|
entry.merge!(params)
|
@@ -311,6 +365,54 @@ class Webhookdb::Replicator::IcalendarEventV1 < Webhookdb::Replicator::Base
|
|
311
365
|
return
|
312
366
|
end
|
313
367
|
|
368
|
+
# Delete CANCELLED events last updated (+row_updated_at+)' in the window between
|
369
|
+
# +stale_at+ to +age_cutoff+. This avoids endlessly adding to the icalendar events table
|
370
|
+
# due to feeds that change UIDs each fetch- events with changed UIDs will become CANCELLED,
|
371
|
+
# and then deleted over time.
|
372
|
+
# @param stale_at [Time] When an event is considered 'stale'.
|
373
|
+
# If stale events are a big problem, this can be shortened to just a few days.
|
374
|
+
# @param age_cutoff [Time] Where to stop searching for old events.
|
375
|
+
# This is important to avoid a full table scale when deleting events,
|
376
|
+
# since otherwise it is like 'row_updated_at < 35.days.ago'.
|
377
|
+
# Since this routine should run regularly, we should rarely have events more than 35 or 36 days old,
|
378
|
+
# for example.
|
379
|
+
# Use +nil+ to use no limit (a full table scan) which may be necessary when running this feature
|
380
|
+
# for the first time.
|
381
|
+
# @param chunk_size [Integer] The row delete is done in chunks to avoid long locks.
|
382
|
+
# The default seems safe, but it's exposed as a parameter if you need to play around with it,
|
383
|
+
# and can be done via configuration if needed at some point.
|
384
|
+
def delete_stale_cancelled_events(
|
385
|
+
stale_at: Webhookdb::Icalendar.stale_cancelled_event_threshold_days.days.ago,
|
386
|
+
age_cutoff: (Webhookdb::Icalendar.stale_cancelled_event_threshold_days + 10).days.ago,
|
387
|
+
chunk_size: 10_000
|
388
|
+
)
|
389
|
+
# Delete in chunks, like:
|
390
|
+
# DELETE from "public"."icalendar_event_v1_aaaa"
|
391
|
+
# WHERE pk IN (
|
392
|
+
# SELECT pk FROM "public"."icalendar_event_v1_aaaa"
|
393
|
+
# WHERE row_updated_at < (now() - '35 days'::interval)
|
394
|
+
# LIMIT 10000
|
395
|
+
# )
|
396
|
+
age = age_cutoff..stale_at
|
397
|
+
self.admin_dataset do |ds|
|
398
|
+
chunk_ds = ds.where(row_updated_at: age, status: "CANCELLED").select(:pk).limit(chunk_size)
|
399
|
+
loop do
|
400
|
+
# Due to conflicts where a feed is being inserted while the delete is happening,
|
401
|
+
# this may raise an error like:
|
402
|
+
# deadlock detected
|
403
|
+
# DETAIL: Process 18352 waits for ShareLock on transaction 435085606; blocked by process 24191.
|
404
|
+
# Process 24191 waits for ShareLock on transaction 435085589; blocked by process 18352.
|
405
|
+
# HINT: See server log for query details.
|
406
|
+
# CONTEXT: while deleting tuple (2119119,3) in relation "icalendar_event_v1_aaaa"
|
407
|
+
# Unit testing this is very difficult though, and in practice it is rare,
|
408
|
+
# and normal Sidekiq job retries should be sufficient to handle this.
|
409
|
+
# So we don't explicitly handle deadlocks, but could if it becomes an issue.
|
410
|
+
deleted = ds.where(pk: chunk_ds).delete
|
411
|
+
break if deleted != chunk_size
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
314
416
|
def calculate_webhook_state_machine
|
315
417
|
if (step = self.calculate_dependency_state_machine_step(dependency_help: ""))
|
316
418
|
return step
|
@@ -11,12 +11,12 @@ class Webhookdb::Replicator::IncreaseAccountNumberV1 < Webhookdb::Replicator::Ba
|
|
11
11
|
def self.descriptor
|
12
12
|
return Webhookdb::Replicator::Descriptor.new(
|
13
13
|
name: "increase_account_number_v1",
|
14
|
-
ctor:
|
14
|
+
ctor: self,
|
15
15
|
feature_roles: [],
|
16
16
|
resource_name_singular: "Increase Account Number",
|
17
|
-
|
17
|
+
dependency_descriptor: Webhookdb::Replicator::IncreaseAppV1.descriptor,
|
18
18
|
supports_backfill: true,
|
19
|
-
api_docs_url: "https://
|
19
|
+
api_docs_url: "https://increase.com/documentation/api",
|
20
20
|
)
|
21
21
|
end
|
22
22
|
|
@@ -26,52 +26,15 @@ class Webhookdb::Replicator::IncreaseAccountNumberV1 < Webhookdb::Replicator::Ba
|
|
26
26
|
|
27
27
|
def _denormalized_columns
|
28
28
|
return [
|
29
|
+
Webhookdb::Replicator::Column.new(:created_at, TIMESTAMP, index: true),
|
30
|
+
Webhookdb::Replicator::Column.new(:updated_at, TIMESTAMP, index: true),
|
29
31
|
Webhookdb::Replicator::Column.new(:account_id, TEXT, index: true),
|
30
32
|
Webhookdb::Replicator::Column.new(:account_number, TEXT, index: true),
|
31
33
|
Webhookdb::Replicator::Column.new(:name, TEXT),
|
32
34
|
Webhookdb::Replicator::Column.new(:routing_number, TEXT, index: true),
|
33
|
-
Webhookdb::Replicator::Column.new(
|
34
|
-
:row_created_at,
|
35
|
-
TIMESTAMP,
|
36
|
-
data_key: "created_at",
|
37
|
-
event_key: "created_at",
|
38
|
-
optional: true,
|
39
|
-
defaulter: :now,
|
40
|
-
index: true,
|
41
|
-
),
|
42
|
-
Webhookdb::Replicator::Column.new(
|
43
|
-
:row_updated_at,
|
44
|
-
TIMESTAMP,
|
45
|
-
data_key: "created_at",
|
46
|
-
event_key: "created_at",
|
47
|
-
defaulter: :now,
|
48
|
-
optional: true,
|
49
|
-
index: true,
|
50
|
-
),
|
51
35
|
Webhookdb::Replicator::Column.new(:status, TEXT),
|
52
36
|
]
|
53
37
|
end
|
54
38
|
|
55
|
-
def
|
56
|
-
return :row_updated_at
|
57
|
-
end
|
58
|
-
|
59
|
-
def _update_where_expr
|
60
|
-
return self.qualified_table_sequel_identifier[:row_updated_at] < Sequel[:excluded][:row_updated_at]
|
61
|
-
end
|
62
|
-
|
63
|
-
def _resource_and_event(request)
|
64
|
-
return self._find_resource_and_event(request.body, "account_number")
|
65
|
-
end
|
66
|
-
|
67
|
-
def _upsert_update_expr(inserting, **_kwargs)
|
68
|
-
update = super
|
69
|
-
# Only set created_at if it's not set so the initial insert isn't modified.
|
70
|
-
self._coalesce_excluded_on_update(update, [:row_created_at])
|
71
|
-
return update
|
72
|
-
end
|
73
|
-
|
74
|
-
def _mixin_backfill_url
|
75
|
-
return "#{self.service_integration.api_url}/account_numbers"
|
76
|
-
end
|
39
|
+
def _mixin_object_type = "account_number"
|
77
40
|
end
|
@@ -11,10 +11,10 @@ class Webhookdb::Replicator::IncreaseAccountTransferV1 < Webhookdb::Replicator::
|
|
11
11
|
def self.descriptor
|
12
12
|
return Webhookdb::Replicator::Descriptor.new(
|
13
13
|
name: "increase_account_transfer_v1",
|
14
|
-
ctor:
|
14
|
+
ctor: self,
|
15
15
|
feature_roles: [],
|
16
16
|
resource_name_singular: "Increase Account Transfer",
|
17
|
-
|
17
|
+
dependency_descriptor: Webhookdb::Replicator::IncreaseAppV1.descriptor,
|
18
18
|
supports_backfill: true,
|
19
19
|
api_docs_url: "https://increase.com/documentation/api",
|
20
20
|
)
|
@@ -28,34 +28,17 @@ class Webhookdb::Replicator::IncreaseAccountTransferV1 < Webhookdb::Replicator::
|
|
28
28
|
return [
|
29
29
|
Webhookdb::Replicator::Column.new(:amount, INTEGER, index: true),
|
30
30
|
Webhookdb::Replicator::Column.new(:account_id, TEXT, index: true),
|
31
|
-
Webhookdb::Replicator::Column.new(:canceled_at, TIMESTAMP, data_key: ["cancellation", "canceled_at"],
|
32
|
-
optional: true,),
|
33
31
|
Webhookdb::Replicator::Column.new(:created_at, TIMESTAMP, index: true),
|
32
|
+
Webhookdb::Replicator::Column.new(:updated_at, TIMESTAMP, index: true),
|
33
|
+
Webhookdb::Replicator::Column.new(
|
34
|
+
:canceled_at, TIMESTAMP, data_key: ["cancellation", "canceled_at"], optional: true,
|
35
|
+
),
|
34
36
|
Webhookdb::Replicator::Column.new(:destination_account_id, TEXT, index: true),
|
35
37
|
Webhookdb::Replicator::Column.new(:destination_transaction_id, TEXT, index: true),
|
36
38
|
Webhookdb::Replicator::Column.new(:status, TEXT),
|
37
|
-
Webhookdb::Replicator::Column.new(:template_id, TEXT),
|
38
39
|
Webhookdb::Replicator::Column.new(:transaction_id, TEXT, index: true),
|
39
|
-
Webhookdb::Replicator::Column.new(
|
40
|
-
:updated_at,
|
41
|
-
TIMESTAMP,
|
42
|
-
data_key: "created_at",
|
43
|
-
event_key: "created_at",
|
44
|
-
defaulter: :now,
|
45
|
-
index: true,
|
46
|
-
),
|
47
40
|
]
|
48
41
|
end
|
49
42
|
|
50
|
-
def
|
51
|
-
return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
|
52
|
-
end
|
53
|
-
|
54
|
-
def _resource_and_event(request)
|
55
|
-
return self._find_resource_and_event(request.body, "account_transfer")
|
56
|
-
end
|
57
|
-
|
58
|
-
def _mixin_backfill_url
|
59
|
-
return "#{self.service_integration.api_url}/account_transfers"
|
60
|
-
end
|
43
|
+
def _mixin_object_type = "account_transfer"
|
61
44
|
end
|
@@ -11,10 +11,10 @@ class Webhookdb::Replicator::IncreaseAccountV1 < Webhookdb::Replicator::Base
|
|
11
11
|
def self.descriptor
|
12
12
|
return Webhookdb::Replicator::Descriptor.new(
|
13
13
|
name: "increase_account_v1",
|
14
|
-
ctor:
|
14
|
+
ctor: self,
|
15
15
|
feature_roles: [],
|
16
16
|
resource_name_singular: "Increase Account",
|
17
|
-
|
17
|
+
dependency_descriptor: Webhookdb::Replicator::IncreaseAppV1.descriptor,
|
18
18
|
supports_backfill: true,
|
19
19
|
api_docs_url: "https://increase.com/documentation/api",
|
20
20
|
)
|
@@ -26,38 +26,14 @@ class Webhookdb::Replicator::IncreaseAccountV1 < Webhookdb::Replicator::Base
|
|
26
26
|
|
27
27
|
def _denormalized_columns
|
28
28
|
return [
|
29
|
-
Webhookdb::Replicator::Column.new(:
|
30
|
-
Webhookdb::Replicator::Column.new(
|
31
|
-
:created_at,
|
32
|
-
TIMESTAMP,
|
33
|
-
data_key: "created_at",
|
34
|
-
defaulter: :now,
|
35
|
-
index: true,
|
36
|
-
),
|
37
|
-
Webhookdb::Replicator::Column.new(:entity_id, TEXT, index: true),
|
38
|
-
Webhookdb::Replicator::Column.new(:interest_accrued, DECIMAL),
|
29
|
+
Webhookdb::Replicator::Column.new(:created_at, TIMESTAMP, index: true),
|
30
|
+
Webhookdb::Replicator::Column.new(:updated_at, TIMESTAMP, index: true),
|
39
31
|
Webhookdb::Replicator::Column.new(:name, TEXT),
|
32
|
+
Webhookdb::Replicator::Column.new(:entity_id, TEXT, index: true),
|
40
33
|
Webhookdb::Replicator::Column.new(:status, TEXT),
|
41
|
-
Webhookdb::Replicator::Column.new(
|
42
|
-
:updated_at,
|
43
|
-
TIMESTAMP,
|
44
|
-
data_key: "created_at",
|
45
|
-
event_key: "created_at",
|
46
|
-
defaulter: :now,
|
47
|
-
index: true,
|
48
|
-
),
|
34
|
+
Webhookdb::Replicator::Column.new(:interest_accrued, DECIMAL),
|
49
35
|
]
|
50
36
|
end
|
51
37
|
|
52
|
-
def
|
53
|
-
return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
|
54
|
-
end
|
55
|
-
|
56
|
-
def _resource_and_event(request)
|
57
|
-
return self._find_resource_and_event(request.body, "account")
|
58
|
-
end
|
59
|
-
|
60
|
-
def _mixin_backfill_url
|
61
|
-
return "#{self.service_integration.api_url}/accounts"
|
62
|
-
end
|
38
|
+
def _mixin_object_type = "account"
|
63
39
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "webhookdb/increase"
|
4
3
|
require "webhookdb/replicator/increase_v1_mixin"
|
5
4
|
|
6
5
|
class Webhookdb::Replicator::IncreaseACHTransferV1 < Webhookdb::Replicator::Base
|
@@ -11,10 +10,10 @@ class Webhookdb::Replicator::IncreaseACHTransferV1 < Webhookdb::Replicator::Base
|
|
11
10
|
def self.descriptor
|
12
11
|
return Webhookdb::Replicator::Descriptor.new(
|
13
12
|
name: "increase_ach_transfer_v1",
|
14
|
-
ctor:
|
13
|
+
ctor: self,
|
15
14
|
feature_roles: [],
|
16
15
|
resource_name_singular: "Increase ACH Transfer",
|
17
|
-
|
16
|
+
dependency_descriptor: Webhookdb::Replicator::IncreaseAppV1.descriptor,
|
18
17
|
supports_backfill: true,
|
19
18
|
api_docs_url: "https://increase.com/documentation/api",
|
20
19
|
)
|
@@ -29,50 +28,13 @@ class Webhookdb::Replicator::IncreaseACHTransferV1 < Webhookdb::Replicator::Base
|
|
29
28
|
Webhookdb::Replicator::Column.new(:account_number, TEXT, index: true),
|
30
29
|
Webhookdb::Replicator::Column.new(:account_id, TEXT, index: true),
|
31
30
|
Webhookdb::Replicator::Column.new(:amount, INTEGER, index: true),
|
32
|
-
Webhookdb::Replicator::Column.new(
|
33
|
-
|
34
|
-
TIMESTAMP,
|
35
|
-
data_key: "created_at",
|
36
|
-
index: true,
|
37
|
-
),
|
31
|
+
Webhookdb::Replicator::Column.new(:created_at, TIMESTAMP, index: true),
|
32
|
+
Webhookdb::Replicator::Column.new(:updated_at, TIMESTAMP, index: true),
|
38
33
|
Webhookdb::Replicator::Column.new(:routing_number, TEXT, index: true),
|
39
34
|
Webhookdb::Replicator::Column.new(:status, TEXT),
|
40
35
|
Webhookdb::Replicator::Column.new(:transaction_id, TEXT, index: true),
|
41
|
-
Webhookdb::Replicator::Column.new(
|
42
|
-
:updated_at,
|
43
|
-
TIMESTAMP,
|
44
|
-
data_key: "created_at",
|
45
|
-
event_key: "created_at",
|
46
|
-
defaulter: :now,
|
47
|
-
index: true,
|
48
|
-
),
|
49
36
|
]
|
50
37
|
end
|
51
38
|
|
52
|
-
def
|
53
|
-
# created_at is marked required, but to skip on nil.
|
54
|
-
# This will preserve its existing value when we update the webhook.
|
55
|
-
resource["created_at"] = nil if event&.fetch("event") == "updated"
|
56
|
-
return super
|
57
|
-
end
|
58
|
-
|
59
|
-
def _upsert_update_expr(inserting, enrichment: nil)
|
60
|
-
update = super
|
61
|
-
update[:data] = Sequel.lit("#{self.service_integration.table_name}.data || excluded.data")
|
62
|
-
return update
|
63
|
-
end
|
64
|
-
|
65
|
-
def _update_where_expr
|
66
|
-
return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
|
67
|
-
end
|
68
|
-
|
69
|
-
def _resource_and_event(request)
|
70
|
-
resource, event = self._find_resource_and_event(request.body, "ach_transfer")
|
71
|
-
return nil, nil if (resource && resource["type"]) == "inbound_ach_transfer"
|
72
|
-
return resource, event
|
73
|
-
end
|
74
|
-
|
75
|
-
def _mixin_backfill_url
|
76
|
-
return "#{self.service_integration.api_url}/transfers/achs"
|
77
|
-
end
|
39
|
+
def _mixin_object_type = "ach_transfer"
|
78
40
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Webhookdb::Replicator::IncreaseAppV1 < Webhookdb::Replicator::Base
|
4
|
+
include Appydays::Loggable
|
5
|
+
|
6
|
+
# @return [Webhookdb::Replicator::Descriptor]
|
7
|
+
def self.descriptor
|
8
|
+
return Webhookdb::Replicator::Descriptor.new(
|
9
|
+
name: "increase_app_v1",
|
10
|
+
ctor: self,
|
11
|
+
feature_roles: [],
|
12
|
+
resource_name_singular: "Increase App",
|
13
|
+
resource_name_plural: "Increase App",
|
14
|
+
supports_webhooks: true,
|
15
|
+
supports_backfill: true,
|
16
|
+
install_url: "#{Webhookdb.api_url}/increase",
|
17
|
+
documentation_url: "https://docs.webhookdb.com/guides/increase/",
|
18
|
+
description: "Replicate your Increase data to WebhookDB Cloud in one click using " \
|
19
|
+
"our [WebhookDB-Increase integration](https://docs.webhookdb.com/guides/increase).",
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def _remote_key_column
|
24
|
+
return Webhookdb::Replicator::Column.new(:ignore_id, INTEGER)
|
25
|
+
end
|
26
|
+
|
27
|
+
def _denormalized_columns
|
28
|
+
return []
|
29
|
+
end
|
30
|
+
|
31
|
+
def _upsert_webhook(request, **kw)
|
32
|
+
raise Webhookdb::InvalidPrecondition, "can only handle event payloads" unless request.body.fetch("type") == "event"
|
33
|
+
dispatchable = self.service_integration.dependents.select do |d|
|
34
|
+
d.service_name == "increase_event_v1" || d.replicator.handle_event?(request.body)
|
35
|
+
end
|
36
|
+
dispatchable.each do |sint|
|
37
|
+
sint.replicator.upsert_webhook(request, **kw)
|
38
|
+
end
|
39
|
+
return nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def _fetch_backfill_page(*)
|
43
|
+
return [], nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def webhook_response(request)
|
47
|
+
return Webhookdb::Increase.webhook_response(request, Webhookdb::Increase.webhook_secret)
|
48
|
+
end
|
49
|
+
|
50
|
+
def calculate_webhook_state_machine
|
51
|
+
return self.calculate_backfill_state_machine
|
52
|
+
end
|
53
|
+
|
54
|
+
def calculate_backfill_state_machine
|
55
|
+
step = Webhookdb::Replicator::StateMachineStep.new
|
56
|
+
step.output = %(This replicator is managed automatically using OAuth through Increase.
|
57
|
+
Head over to #{self.descriptor.install_url} to learn more.)
|
58
|
+
step.completed
|
59
|
+
return step
|
60
|
+
end
|
61
|
+
|
62
|
+
def build_dependents
|
63
|
+
org = self.service_integration.organization
|
64
|
+
parent_descr = self.descriptor
|
65
|
+
sints = self.service_integration.organization.available_replicators.
|
66
|
+
select { |dd| dd.dependency_descriptor == parent_descr }.
|
67
|
+
map do |dd|
|
68
|
+
Webhookdb::ServiceIntegration.create_disambiguated(
|
69
|
+
dd.name,
|
70
|
+
organization: org,
|
71
|
+
depends_on: self.service_integration,
|
72
|
+
)
|
73
|
+
end
|
74
|
+
sints.
|
75
|
+
select { |sint| sint.replicator.descriptor.supports_backfill? }.
|
76
|
+
each { |sint| sint.replicator._enqueue_backfill_jobs(incremental: false) }
|
77
|
+
end
|
78
|
+
end
|
@@ -11,10 +11,10 @@ class Webhookdb::Replicator::IncreaseCheckTransferV1 < Webhookdb::Replicator::Ba
|
|
11
11
|
def self.descriptor
|
12
12
|
return Webhookdb::Replicator::Descriptor.new(
|
13
13
|
name: "increase_check_transfer_v1",
|
14
|
-
ctor:
|
14
|
+
ctor: self,
|
15
15
|
feature_roles: [],
|
16
16
|
resource_name_singular: "Increase Check Transfer",
|
17
|
-
|
17
|
+
dependency_descriptor: Webhookdb::Replicator::IncreaseAppV1.descriptor,
|
18
18
|
supports_backfill: true,
|
19
19
|
api_docs_url: "https://increase.com/documentation/api",
|
20
20
|
)
|
@@ -26,39 +26,33 @@ class Webhookdb::Replicator::IncreaseCheckTransferV1 < Webhookdb::Replicator::Ba
|
|
26
26
|
|
27
27
|
def _denormalized_columns
|
28
28
|
return [
|
29
|
+
Webhookdb::Replicator::Column.new(:created_at, TIMESTAMP, index: true),
|
30
|
+
Webhookdb::Replicator::Column.new(:updated_at, TIMESTAMP, index: true),
|
29
31
|
Webhookdb::Replicator::Column.new(:account_id, TEXT, index: true),
|
30
|
-
Webhookdb::Replicator::Column.new(:address_line1, TEXT),
|
31
|
-
Webhookdb::Replicator::Column.new(:address_city, TEXT),
|
32
|
-
Webhookdb::Replicator::Column.new(:address_state, TEXT),
|
33
|
-
Webhookdb::Replicator::Column.new(:address_zip, TEXT, index: true),
|
34
32
|
Webhookdb::Replicator::Column.new(:amount, INTEGER, index: true),
|
35
|
-
Webhookdb::Replicator::Column.new(:
|
36
|
-
Webhookdb::Replicator::Column.new(:
|
37
|
-
Webhookdb::Replicator::Column.new(:
|
33
|
+
Webhookdb::Replicator::Column.new(:account_number, TEXT, index: true),
|
34
|
+
Webhookdb::Replicator::Column.new(:routing_number, TEXT, index: true),
|
35
|
+
Webhookdb::Replicator::Column.new(:check_number, TEXT, index: true),
|
36
|
+
Webhookdb::Replicator::Column.new(
|
37
|
+
:recipient_name,
|
38
|
+
TEXT,
|
39
|
+
data_key: ["physical_check", "recipient_name"], optional: true,
|
40
|
+
),
|
38
41
|
Webhookdb::Replicator::Column.new(:status, TEXT),
|
39
|
-
Webhookdb::Replicator::Column.new(:submitted_at, TIMESTAMP, index: true),
|
40
|
-
Webhookdb::Replicator::Column.new(:template_id, TEXT),
|
41
|
-
Webhookdb::Replicator::Column.new(:transaction_id, TEXT, index: true),
|
42
42
|
Webhookdb::Replicator::Column.new(
|
43
|
-
:
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
:canceled_at, TIMESTAMP, data_key: ["cancellation", "canceled_at"], optional: true, index: true,
|
44
|
+
),
|
45
|
+
Webhookdb::Replicator::Column.new(
|
46
|
+
:deposited_at, TIMESTAMP, data_key: ["deposit", "deposited_at"], optional: true, index: true,
|
47
|
+
),
|
48
|
+
Webhookdb::Replicator::Column.new(
|
49
|
+
:mailed_at, TIMESTAMP, data_key: ["mailing", "mailed_at"], optional: true, index: true,
|
50
|
+
),
|
51
|
+
Webhookdb::Replicator::Column.new(
|
52
|
+
:submitted_at, TIMESTAMP, data_key: ["submission", "submitted_at"], optional: true, index: true,
|
49
53
|
),
|
50
54
|
]
|
51
55
|
end
|
52
56
|
|
53
|
-
def
|
54
|
-
return self.qualified_table_sequel_identifier[:updated_at] < Sequel[:excluded][:updated_at]
|
55
|
-
end
|
56
|
-
|
57
|
-
def _resource_and_event(request)
|
58
|
-
return self._find_resource_and_event(request.body, "check_transfer")
|
59
|
-
end
|
60
|
-
|
61
|
-
def _mixin_backfill_url
|
62
|
-
return "#{self.service_integration.api_url}/check_transfers"
|
63
|
-
end
|
57
|
+
def _mixin_object_type = "check_transfer"
|
64
58
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Webhookdb::Replicator::IncreaseEventV1 < Webhookdb::Replicator::Base
|
4
|
+
include Appydays::Loggable
|
5
|
+
include Webhookdb::Replicator::IncreaseV1Mixin
|
6
|
+
|
7
|
+
# @return [Webhookdb::Replicator::Descriptor]
|
8
|
+
def self.descriptor
|
9
|
+
return Webhookdb::Replicator::Descriptor.new(
|
10
|
+
name: "increase_event_v1",
|
11
|
+
ctor: self,
|
12
|
+
feature_roles: [],
|
13
|
+
resource_name_singular: "Increase Event",
|
14
|
+
dependency_descriptor: Webhookdb::Replicator::IncreaseAppV1.descriptor,
|
15
|
+
# Since events are only done through the increase_app_v1,
|
16
|
+
# we don't support normal WebhookDB webhooks. Instead,
|
17
|
+
# they come in via the app. If we wanted to handle webhooks to the normal
|
18
|
+
# /v1/service_integrations/:opaque_id endpoint, rather than /v1/install/increase/webhook,
|
19
|
+
# we'd make this 'true' and have to do work like webhook validation.
|
20
|
+
# supports_webhooks: false,
|
21
|
+
supports_backfill: true,
|
22
|
+
api_docs_url: "https://increase.com/documentation/api",
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def _remote_key_column
|
27
|
+
return Webhookdb::Replicator::Column.new(:increase_id, TEXT, data_key: "id")
|
28
|
+
end
|
29
|
+
|
30
|
+
def _denormalized_columns
|
31
|
+
return [
|
32
|
+
Webhookdb::Replicator::Column.new(:associated_object_id, TEXT, index: true),
|
33
|
+
Webhookdb::Replicator::Column.new(:associated_object_type, TEXT),
|
34
|
+
Webhookdb::Replicator::Column.new(:category, TEXT, index: true),
|
35
|
+
Webhookdb::Replicator::Column.new(:created_at, TIMESTAMP, index: true),
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
def _timestamp_column_name = :created_at
|
40
|
+
def _mixin_object_type = "event"
|
41
|
+
end
|
@@ -11,10 +11,13 @@ class Webhookdb::Replicator::IncreaseLimitV1 < Webhookdb::Replicator::Base
|
|
11
11
|
def self.descriptor
|
12
12
|
return Webhookdb::Replicator::Descriptor.new(
|
13
13
|
name: "increase_limit_v1",
|
14
|
-
ctor:
|
15
|
-
|
14
|
+
ctor: self,
|
15
|
+
# This is a legacy resource. Instead, users should set the 'allow/deny ACH debits' flag on account numbers,
|
16
|
+
# or use the Inbound ACH Transfer object, which can send a webhook to accept or reject it.
|
17
|
+
# This flag is here for WebhookDB users who still need access to Limit resources.
|
18
|
+
feature_roles: ["increase_limits"],
|
16
19
|
resource_name_singular: "Increase Limit",
|
17
|
-
|
20
|
+
dependency_descriptor: Webhookdb::Replicator::IncreaseAppV1.descriptor,
|
18
21
|
supports_backfill: true,
|
19
22
|
api_docs_url: "https://increase.com/documentation/api",
|
20
23
|
)
|
@@ -34,45 +37,17 @@ class Webhookdb::Replicator::IncreaseLimitV1 < Webhookdb::Replicator::Base
|
|
34
37
|
:row_created_at,
|
35
38
|
TIMESTAMP,
|
36
39
|
data_key: "created_at",
|
37
|
-
event_key: "created_at",
|
38
|
-
defaulter: :now,
|
39
|
-
optional: true,
|
40
|
-
index: true,
|
41
|
-
),
|
42
|
-
Webhookdb::Replicator::Column.new(
|
43
|
-
:row_updated_at,
|
44
|
-
TIMESTAMP,
|
45
|
-
data_key: "created_at",
|
46
|
-
event_key: "created_at",
|
47
40
|
defaulter: :now,
|
48
41
|
optional: true,
|
49
42
|
index: true,
|
50
43
|
),
|
44
|
+
Webhookdb::Replicator::Column.new(:row_updated_at, TIMESTAMP, data_key: "updated_at", index: true),
|
51
45
|
Webhookdb::Replicator::Column.new(:status, TEXT),
|
52
46
|
Webhookdb::Replicator::Column.new(:value, INTEGER),
|
53
47
|
]
|
54
48
|
end
|
55
49
|
|
56
|
-
def _timestamp_column_name
|
57
|
-
return :row_updated_at
|
58
|
-
end
|
50
|
+
def _timestamp_column_name = :row_updated_at
|
59
51
|
|
60
|
-
def
|
61
|
-
return self.qualified_table_sequel_identifier[:row_updated_at] < Sequel[:excluded][:row_updated_at]
|
62
|
-
end
|
63
|
-
|
64
|
-
def _resource_and_event(request)
|
65
|
-
return self._find_resource_and_event(request.body, "limit")
|
66
|
-
end
|
67
|
-
|
68
|
-
def _upsert_update_expr(inserting, **_kwargs)
|
69
|
-
update = super
|
70
|
-
# Only set created_at if it's not set so the initial insert isn't modified.
|
71
|
-
self._coalesce_excluded_on_update(update, [:row_created_at])
|
72
|
-
return update
|
73
|
-
end
|
74
|
-
|
75
|
-
def _mixin_backfill_url
|
76
|
-
return "#{self.service_integration.api_url}/limits"
|
77
|
-
end
|
52
|
+
def _mixin_object_type = "limit"
|
78
53
|
end
|