webhookdb 1.3.1 → 1.5.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 +4 -4
- data/admin-dist/assets/{index-6aebf805.js → index-9306dd28.js} +39 -39
- data/admin-dist/index.html +1 -1
- data/data/messages/templates/errors/generic_backfill.email.liquid +30 -0
- data/data/messages/templates/errors/icalendar_fetch.email.liquid +8 -2
- data/data/messages/templates/specs/with_fields.email.liquid +6 -0
- data/db/migrations/026_undo_integration_backfill_cursor.rb +2 -0
- data/db/migrations/032_remove_db_defaults.rb +2 -0
- data/db/migrations/043_text_search.rb +2 -0
- data/db/migrations/045_system_log.rb +15 -0
- data/db/migrations/046_indices.rb +14 -0
- data/db/migrations/047_sync_parallelism.rb +9 -0
- data/db/migrations/048_sync_stats.rb +9 -0
- data/db/migrations/049_error_handlers.rb +18 -0
- data/db/migrations/050_logged_webhook_indices.rb +25 -0
- data/db/migrations/051_partitioning.rb +9 -0
- data/integration/async_spec.rb +0 -2
- data/integration/service_integrations_spec.rb +0 -2
- data/lib/amigo/durable_job.rb +2 -2
- data/lib/amigo/job_in_context.rb +12 -0
- data/lib/webhookdb/admin.rb +6 -0
- data/lib/webhookdb/admin_api/data_provider.rb +1 -0
- data/lib/webhookdb/admin_api/entities.rb +8 -0
- data/lib/webhookdb/aggregate_result.rb +1 -1
- data/lib/webhookdb/api/entities.rb +6 -2
- data/lib/webhookdb/api/error_handlers.rb +104 -0
- data/lib/webhookdb/api/helpers.rb +25 -1
- data/lib/webhookdb/api/icalproxy.rb +22 -0
- data/lib/webhookdb/api/install.rb +2 -1
- data/lib/webhookdb/api/organizations.rb +6 -0
- data/lib/webhookdb/api/saved_queries.rb +1 -0
- data/lib/webhookdb/api/saved_views.rb +1 -0
- data/lib/webhookdb/api/service_integrations.rb +2 -1
- data/lib/webhookdb/api/sync_targets.rb +1 -1
- data/lib/webhookdb/api/system.rb +5 -0
- data/lib/webhookdb/api/webhook_subscriptions.rb +1 -0
- data/lib/webhookdb/api.rb +4 -1
- data/lib/webhookdb/apps.rb +4 -0
- data/lib/webhookdb/async/autoscaler.rb +10 -0
- data/lib/webhookdb/async/job.rb +4 -0
- data/lib/webhookdb/async/scheduled_job.rb +4 -0
- data/lib/webhookdb/async.rb +2 -0
- data/lib/webhookdb/backfiller.rb +17 -4
- data/lib/webhookdb/concurrent.rb +96 -0
- data/lib/webhookdb/connection_cache.rb +57 -10
- data/lib/webhookdb/console.rb +1 -1
- data/lib/webhookdb/customer/reset_code.rb +1 -1
- data/lib/webhookdb/customer.rb +5 -4
- data/lib/webhookdb/database_document.rb +1 -1
- data/lib/webhookdb/db_adapter/default_sql.rb +1 -14
- data/lib/webhookdb/db_adapter/partition.rb +14 -0
- data/lib/webhookdb/db_adapter/partitioning.rb +8 -0
- data/lib/webhookdb/db_adapter/pg.rb +77 -5
- data/lib/webhookdb/db_adapter/snowflake.rb +15 -6
- data/lib/webhookdb/db_adapter.rb +25 -3
- data/lib/webhookdb/dbutil.rb +2 -0
- data/lib/webhookdb/errors.rb +34 -0
- data/lib/webhookdb/fixtures/logged_webhooks.rb +4 -0
- data/lib/webhookdb/fixtures/organization_error_handlers.rb +20 -0
- data/lib/webhookdb/http.rb +30 -16
- data/lib/webhookdb/icalendar.rb +30 -9
- data/lib/webhookdb/jobs/amigo_test_jobs.rb +1 -1
- data/lib/webhookdb/jobs/backfill.rb +21 -25
- data/lib/webhookdb/jobs/create_mirror_table.rb +3 -4
- data/lib/webhookdb/jobs/deprecated_jobs.rb +3 -0
- data/lib/webhookdb/jobs/emailer.rb +2 -1
- data/lib/webhookdb/jobs/front_signalwire_message_channel_sync_inbound.rb +15 -0
- data/lib/webhookdb/jobs/icalendar_delete_stale_cancelled_events.rb +7 -2
- data/lib/webhookdb/jobs/icalendar_enqueue_syncs.rb +74 -11
- data/lib/webhookdb/jobs/icalendar_enqueue_syncs_for_urls.rb +22 -0
- data/lib/webhookdb/jobs/icalendar_sync.rb +21 -9
- data/lib/webhookdb/jobs/increase_event_handler.rb +3 -2
- data/lib/webhookdb/jobs/{logged_webhook_replay.rb → logged_webhooks_replay.rb} +5 -3
- data/lib/webhookdb/jobs/message_dispatched.rb +1 -0
- data/lib/webhookdb/jobs/model_event_system_log_tracker.rb +112 -0
- data/lib/webhookdb/jobs/monitor_metrics.rb +29 -0
- data/lib/webhookdb/jobs/organization_database_migration_notify.rb +32 -0
- data/lib/webhookdb/jobs/organization_database_migration_run.rb +4 -6
- data/lib/webhookdb/jobs/organization_error_handler_dispatch.rb +26 -0
- data/lib/webhookdb/jobs/prepare_database_connections.rb +1 -0
- data/lib/webhookdb/jobs/process_webhook.rb +11 -12
- data/lib/webhookdb/jobs/renew_watch_channel.rb +10 -10
- data/lib/webhookdb/jobs/replication_migration.rb +5 -2
- data/lib/webhookdb/jobs/reset_code_create_dispatch.rb +1 -2
- data/lib/webhookdb/jobs/scheduled_backfills.rb +2 -2
- data/lib/webhookdb/jobs/send_invite.rb +3 -2
- data/lib/webhookdb/jobs/send_test_webhook.rb +1 -3
- data/lib/webhookdb/jobs/send_webhook.rb +4 -5
- data/lib/webhookdb/jobs/stale_row_deleter.rb +31 -0
- data/lib/webhookdb/jobs/sync_target_enqueue_scheduled.rb +3 -0
- data/lib/webhookdb/jobs/sync_target_run_sync.rb +9 -15
- data/lib/webhookdb/jobs/{webhook_subscription_delivery_attempt.rb → webhook_subscription_delivery_event.rb} +5 -8
- data/lib/webhookdb/liquid/expose.rb +1 -1
- data/lib/webhookdb/liquid/filters.rb +1 -1
- data/lib/webhookdb/liquid/partial.rb +2 -2
- data/lib/webhookdb/logged_webhook/resilient.rb +3 -3
- data/lib/webhookdb/logged_webhook.rb +16 -2
- data/lib/webhookdb/message/email_transport.rb +1 -1
- data/lib/webhookdb/message/transport.rb +1 -1
- data/lib/webhookdb/message.rb +55 -4
- data/lib/webhookdb/messages/error_generic_backfill.rb +47 -0
- data/lib/webhookdb/messages/error_icalendar_fetch.rb +5 -0
- data/lib/webhookdb/messages/error_signalwire_send_sms.rb +2 -0
- data/lib/webhookdb/messages/specs.rb +16 -0
- data/lib/webhookdb/organization/alerting.rb +56 -6
- data/lib/webhookdb/organization/database_migration.rb +2 -2
- data/lib/webhookdb/organization/db_builder.rb +5 -4
- data/lib/webhookdb/organization/error_handler.rb +141 -0
- data/lib/webhookdb/organization.rb +76 -10
- data/lib/webhookdb/postgres/model.rb +1 -0
- data/lib/webhookdb/postgres/model_utilities.rb +2 -0
- data/lib/webhookdb/postgres.rb +3 -4
- data/lib/webhookdb/replicator/base.rb +202 -68
- data/lib/webhookdb/replicator/base_stale_row_deleter.rb +165 -0
- data/lib/webhookdb/replicator/column.rb +2 -0
- data/lib/webhookdb/replicator/email_octopus_contact_v1.rb +0 -1
- data/lib/webhookdb/replicator/fake.rb +106 -88
- data/lib/webhookdb/replicator/front_signalwire_message_channel_app_v1.rb +131 -61
- data/lib/webhookdb/replicator/github_repo_v1_mixin.rb +17 -0
- data/lib/webhookdb/replicator/icalendar_calendar_v1.rb +197 -32
- data/lib/webhookdb/replicator/icalendar_event_v1.rb +20 -44
- data/lib/webhookdb/replicator/icalendar_event_v1_partitioned.rb +33 -0
- data/lib/webhookdb/replicator/intercom_contact_v1.rb +1 -0
- data/lib/webhookdb/replicator/intercom_conversation_v1.rb +1 -0
- data/lib/webhookdb/replicator/intercom_v1_mixin.rb +49 -6
- data/lib/webhookdb/replicator/partitionable_mixin.rb +116 -0
- data/lib/webhookdb/replicator/shopify_v1_mixin.rb +1 -1
- data/lib/webhookdb/replicator/signalwire_message_v1.rb +31 -1
- data/lib/webhookdb/replicator/sponsy_v1_mixin.rb +1 -1
- data/lib/webhookdb/replicator/transistor_episode_stats_v1.rb +0 -1
- data/lib/webhookdb/replicator/transistor_episode_v1.rb +11 -5
- data/lib/webhookdb/replicator/webhook_request.rb +8 -0
- data/lib/webhookdb/replicator.rb +6 -3
- data/lib/webhookdb/service/helpers.rb +4 -0
- data/lib/webhookdb/service/middleware.rb +6 -2
- data/lib/webhookdb/service/view_api.rb +1 -1
- data/lib/webhookdb/service.rb +10 -10
- data/lib/webhookdb/service_integration.rb +19 -1
- data/lib/webhookdb/signalwire.rb +1 -1
- data/lib/webhookdb/spec_helpers/async.rb +0 -4
- data/lib/webhookdb/spec_helpers/sentry.rb +32 -0
- data/lib/webhookdb/spec_helpers/shared_examples_for_replicators.rb +239 -64
- data/lib/webhookdb/spec_helpers.rb +1 -0
- data/lib/webhookdb/sync_target.rb +202 -34
- data/lib/webhookdb/system_log_event.rb +9 -0
- data/lib/webhookdb/tasks/admin.rb +1 -1
- data/lib/webhookdb/tasks/annotate.rb +1 -1
- data/lib/webhookdb/tasks/db.rb +13 -1
- data/lib/webhookdb/tasks/docs.rb +1 -1
- data/lib/webhookdb/tasks/fixture.rb +1 -1
- data/lib/webhookdb/tasks/message.rb +1 -1
- data/lib/webhookdb/tasks/regress.rb +1 -1
- data/lib/webhookdb/tasks/release.rb +1 -1
- data/lib/webhookdb/tasks/sidekiq.rb +1 -1
- data/lib/webhookdb/tasks/specs.rb +1 -1
- data/lib/webhookdb/version.rb +1 -1
- data/lib/webhookdb/webhook_subscription.rb +3 -4
- data/lib/webhookdb.rb +34 -8
- metadata +114 -64
- data/lib/webhookdb/jobs/customer_created_notify_internal.rb +0 -22
- data/lib/webhookdb/jobs/organization_database_migration_notify_finished.rb +0 -21
- data/lib/webhookdb/jobs/organization_database_migration_notify_started.rb +0 -21
- /data/lib/webhookdb/jobs/{logged_webhook_resilient_replay.rb → logged_webhooks_resilient_replay.rb} +0 -0
- /data/lib/webhookdb/jobs/{webhook_resource_notify_integrations.rb → webhookdb_resource_notify_integrations.rb} +0 -0
@@ -4,12 +4,18 @@ require "webhookdb/spec_helpers/whdb"
|
|
4
4
|
|
5
5
|
# The basics: these shared examples are among the most commonly used.
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
# @param supports_rowupsert: If true, this replicator will emit the rowupsert event when rows change.
|
8
|
+
# Nearly all replicators support this, but in some cases, replicators may not want to,
|
9
|
+
# especially when they otherwise do not adhere to normal replicator design.
|
10
|
+
# Usually this is only the case in enterprise intergrations.
|
11
|
+
#
|
12
|
+
# @param supports_row_diff: If true, test that the rowupsert event is not emitted when the row has not changed.
|
13
|
+
RSpec.shared_examples "a replicator" do |supports_rowupsert: true, supports_row_diff: true|
|
14
|
+
let(:service_name) { described_class.descriptor.name }
|
15
|
+
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
9
16
|
let(:svc) { Webhookdb::Replicator.create(sint) }
|
10
17
|
let(:body) { raise NotImplementedError }
|
11
18
|
let(:expected_data) { body }
|
12
|
-
let(:supports_row_diff) { true }
|
13
19
|
let(:expected_row) { nil }
|
14
20
|
Webhookdb::SpecHelpers::Whdb.setup_upsert_webhook_example(self)
|
15
21
|
|
@@ -70,48 +76,50 @@ RSpec.shared_examples "a replicator" do |name|
|
|
70
76
|
end
|
71
77
|
end
|
72
78
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
79
|
+
if supports_rowupsert
|
80
|
+
it "emits the rowupsert event if the row has changed", :async, :do_not_defer_events, sidekiq: :fake do
|
81
|
+
Webhookdb::Fixtures.webhook_subscription(service_integration: sint).create
|
82
|
+
svc.create_table
|
83
|
+
upsert_webhook(svc, body:)
|
84
|
+
expect(Sidekiq).to have_queue.consisting_of(
|
85
|
+
job_hash(
|
86
|
+
Webhookdb::Jobs::SendWebhook,
|
87
|
+
args: contain_exactly(
|
88
|
+
hash_including(
|
89
|
+
"id",
|
90
|
+
"name" => "webhookdb.serviceintegration.rowupsert",
|
91
|
+
"payload" => [
|
92
|
+
sint.id,
|
93
|
+
hash_including("external_id", "external_id_column", "row" => hash_including("data")),
|
94
|
+
],
|
95
|
+
),
|
88
96
|
),
|
89
97
|
),
|
90
|
-
)
|
91
|
-
|
92
|
-
end
|
98
|
+
)
|
99
|
+
end
|
93
100
|
|
94
|
-
it "does not emit the rowupsert event if the row has not changed", :async, :do_not_defer_events, sidekiq: :fake do
|
95
101
|
if supports_row_diff
|
96
|
-
|
97
|
-
|
102
|
+
it "does not emit the rowupsert event if the row has not changed", :async, :do_not_defer_events, sidekiq: :fake do
|
103
|
+
Webhookdb::Fixtures.webhook_subscription(service_integration: sint).create
|
104
|
+
expect(Webhookdb::Jobs::SendWebhook).to receive(:perform_async).once
|
105
|
+
svc.create_table
|
106
|
+
upsert_webhook(svc, body:) # Upsert and make sure the next does not run
|
107
|
+
expect do
|
108
|
+
upsert_webhook(svc, body:)
|
109
|
+
end.to_not publish("webhookdb.serviceintegration.rowupsert")
|
110
|
+
expect(Sidekiq).to have_empty_queues
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
it "does not emit the rowupsert event if there are no subscriptions", :async, :do_not_defer_events do
|
115
|
+
# No subscription is created so should not publish
|
98
116
|
svc.create_table
|
99
|
-
upsert_webhook(svc, body:) # Upsert and make sure the next does not run
|
100
117
|
expect do
|
101
118
|
upsert_webhook(svc, body:)
|
102
119
|
end.to_not publish("webhookdb.serviceintegration.rowupsert")
|
103
|
-
expect(Sidekiq).to have_empty_queues
|
104
120
|
end
|
105
121
|
end
|
106
122
|
|
107
|
-
it "does not emit the rowupsert event if there are no subscriptions", :async, :do_not_defer_events do
|
108
|
-
# No subscription is created so should not publish
|
109
|
-
svc.create_table
|
110
|
-
expect do
|
111
|
-
upsert_webhook(svc, body:)
|
112
|
-
end.to_not publish("webhookdb.serviceintegration.rowupsert")
|
113
|
-
end
|
114
|
-
|
115
123
|
it "can serve a webhook response" do
|
116
124
|
create_all_dependencies(sint)
|
117
125
|
request = fake_request
|
@@ -174,7 +182,8 @@ RSpec.shared_examples "a replicator" do |name|
|
|
174
182
|
end
|
175
183
|
end
|
176
184
|
|
177
|
-
RSpec.shared_examples "a replicator with dependents" do |
|
185
|
+
RSpec.shared_examples "a replicator with dependents" do |dependent_service_name|
|
186
|
+
let(:service_name) { described_class.descriptor.name }
|
178
187
|
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
179
188
|
let(:svc) { Webhookdb::Replicator.create(sint) }
|
180
189
|
let(:body) { raise NotImplementedError }
|
@@ -218,7 +227,8 @@ RSpec.shared_examples "a replicator with dependents" do |service_name, dependent
|
|
218
227
|
end
|
219
228
|
end
|
220
229
|
|
221
|
-
RSpec.shared_examples "a replicator dependent on another" do |
|
230
|
+
RSpec.shared_examples "a replicator dependent on another" do |dependency_service_name|
|
231
|
+
let(:service_name) { described_class.descriptor.name }
|
222
232
|
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
223
233
|
let(:svc) { Webhookdb::Replicator.create(sint) }
|
224
234
|
|
@@ -250,8 +260,9 @@ RSpec.shared_examples "a replicator dependent on another" do |service_name, depe
|
|
250
260
|
end
|
251
261
|
end
|
252
262
|
|
253
|
-
RSpec.shared_examples "a replicator that prevents overwriting new data with old" do
|
254
|
-
let(:
|
263
|
+
RSpec.shared_examples "a replicator that prevents overwriting new data with old" do
|
264
|
+
let(:service_name) { described_class.descriptor.name }
|
265
|
+
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
255
266
|
let(:svc) { Webhookdb::Replicator.create(sint) }
|
256
267
|
let(:old_body) { raise NotImplementedError }
|
257
268
|
let(:new_body) { raise NotImplementedError }
|
@@ -313,11 +324,12 @@ RSpec.shared_examples "a replicator that prevents overwriting new data with old"
|
|
313
324
|
end
|
314
325
|
end
|
315
326
|
|
316
|
-
RSpec.shared_examples "a replicator that can backfill" do
|
327
|
+
RSpec.shared_examples "a replicator that can backfill" do
|
317
328
|
let(:api_url) { "https://fake-url.com" }
|
329
|
+
let(:service_name) { described_class.descriptor.name }
|
318
330
|
let(:sint) do
|
319
331
|
Webhookdb::Fixtures.service_integration.create(
|
320
|
-
service_name
|
332
|
+
service_name:,
|
321
333
|
backfill_key: "bfkey",
|
322
334
|
backfill_secret: "bfsek",
|
323
335
|
api_url:,
|
@@ -427,8 +439,9 @@ end
|
|
427
439
|
|
428
440
|
# These shared examples test the way a replicator synthesizes and retrieves information from the API.
|
429
441
|
|
430
|
-
RSpec.shared_examples "a replicator that may have a minimal body" do
|
431
|
-
let(:
|
442
|
+
RSpec.shared_examples "a replicator that may have a minimal body" do
|
443
|
+
let(:service_name) { described_class.descriptor.name }
|
444
|
+
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
432
445
|
let(:svc) { Webhookdb::Replicator.create(sint) }
|
433
446
|
let(:body) { raise NotImplementedError }
|
434
447
|
let(:other_bodies) { [] }
|
@@ -453,8 +466,9 @@ RSpec.shared_examples "a replicator that may have a minimal body" do |name|
|
|
453
466
|
end
|
454
467
|
end
|
455
468
|
|
456
|
-
RSpec.shared_examples "a replicator that deals with resources and wrapped events" do
|
457
|
-
let(:
|
469
|
+
RSpec.shared_examples "a replicator that deals with resources and wrapped events" do
|
470
|
+
let(:service_name) { described_class.descriptor.name }
|
471
|
+
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
458
472
|
let(:svc) { Webhookdb::Replicator.create(sint) }
|
459
473
|
let(:resource_json) { raise NotImplementedError }
|
460
474
|
let(:resource_in_envelope_json) { raise NotImplementedError }
|
@@ -489,8 +503,9 @@ RSpec.shared_examples "a replicator that deals with resources and wrapped events
|
|
489
503
|
end
|
490
504
|
end
|
491
505
|
|
492
|
-
RSpec.shared_examples "a replicator that uses enrichments" do |
|
493
|
-
let(:
|
506
|
+
RSpec.shared_examples "a replicator that uses enrichments" do |stores_enrichment_column: true|
|
507
|
+
let(:service_name) { described_class.descriptor.name }
|
508
|
+
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
494
509
|
let(:svc) { Webhookdb::Replicator.create(sint) }
|
495
510
|
let(:body) { raise NotImplementedError }
|
496
511
|
# Needed if stores_enrichment_column is true
|
@@ -549,8 +564,9 @@ RSpec.shared_examples "a replicator that uses enrichments" do |name, stores_enri
|
|
549
564
|
end
|
550
565
|
end
|
551
566
|
|
552
|
-
RSpec.shared_examples "a replicator that upserts webhooks only under specific conditions" do
|
553
|
-
let(:
|
567
|
+
RSpec.shared_examples "a replicator that upserts webhooks only under specific conditions" do
|
568
|
+
let(:service_name) { described_class.descriptor.name }
|
569
|
+
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
554
570
|
let(:svc) { Webhookdb::Replicator.create(sint) }
|
555
571
|
let(:incorrect_webhook) { raise NotImplementedError }
|
556
572
|
Webhookdb::SpecHelpers::Whdb.setup_upsert_webhook_example(self)
|
@@ -574,8 +590,9 @@ end
|
|
574
590
|
|
575
591
|
# These shared examples can be used to test replicators that support webhooks.
|
576
592
|
|
577
|
-
RSpec.shared_examples "a webhook validating replicator that uses credentials from a dependency" do
|
578
|
-
let(:
|
593
|
+
RSpec.shared_examples "a webhook validating replicator that uses credentials from a dependency" do
|
594
|
+
let(:service_name) { described_class.descriptor.name }
|
595
|
+
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
579
596
|
|
580
597
|
before(:each) do
|
581
598
|
create_all_dependencies(sint)
|
@@ -597,8 +614,9 @@ RSpec.shared_examples "a webhook validating replicator that uses credentials fro
|
|
597
614
|
end
|
598
615
|
end
|
599
616
|
|
600
|
-
RSpec.shared_examples "a replicator that processes webhooks synchronously" do
|
601
|
-
let(:
|
617
|
+
RSpec.shared_examples "a replicator that processes webhooks synchronously" do
|
618
|
+
let(:service_name) { described_class.descriptor.name }
|
619
|
+
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
602
620
|
let(:svc) { Webhookdb::Replicator.create(sint) }
|
603
621
|
let(:expected_synchronous_response) { raise NotImplementedError }
|
604
622
|
Webhookdb::SpecHelpers::Whdb.setup_upsert_webhook_example(self)
|
@@ -620,8 +638,9 @@ end
|
|
620
638
|
|
621
639
|
# These shared examples test the intricacies of backfill logic.
|
622
640
|
|
623
|
-
RSpec.shared_examples "a backfill replicator that requires credentials from a dependency" do
|
624
|
-
let(:
|
641
|
+
RSpec.shared_examples "a backfill replicator that requires credentials from a dependency" do
|
642
|
+
let(:service_name) { described_class.descriptor.name }
|
643
|
+
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
625
644
|
let(:error_message) { raise NotImplementedError }
|
626
645
|
|
627
646
|
before(:each) do
|
@@ -640,12 +659,13 @@ RSpec.shared_examples "a backfill replicator that requires credentials from a de
|
|
640
659
|
end
|
641
660
|
end
|
642
661
|
|
643
|
-
RSpec.shared_examples "a replicator that can backfill incrementally" do
|
662
|
+
RSpec.shared_examples "a replicator that can backfill incrementally" do
|
663
|
+
let(:service_name) { described_class.descriptor.name }
|
644
664
|
let(:last_backfilled) { raise NotImplementedError, "what should the last_backfilled_at value be to start?" }
|
645
665
|
let(:api_url) { "https://fake-url.com" }
|
646
666
|
let(:sint) do
|
647
667
|
Webhookdb::Fixtures.service_integration.create(
|
648
|
-
service_name
|
668
|
+
service_name:,
|
649
669
|
backfill_key: "bfkey",
|
650
670
|
backfill_secret: "bfsek",
|
651
671
|
api_url:,
|
@@ -707,6 +727,86 @@ RSpec.shared_examples "a replicator that can backfill incrementally" do |name|
|
|
707
727
|
end
|
708
728
|
end
|
709
729
|
|
730
|
+
RSpec.shared_examples "a replicator that alerts on backfill auth errors" do
|
731
|
+
let(:service_name) { described_class.descriptor.name }
|
732
|
+
let(:sint_params) { {} }
|
733
|
+
let(:sint) do
|
734
|
+
Webhookdb::Fixtures.service_integration.create(
|
735
|
+
service_name:,
|
736
|
+
backfill_key: "bfkey",
|
737
|
+
backfill_secret: "bfsek",
|
738
|
+
api_url: "https://fake-url.com",
|
739
|
+
**sint_params,
|
740
|
+
)
|
741
|
+
end
|
742
|
+
let(:svc) { Webhookdb::Replicator.create(sint) }
|
743
|
+
let(:template_name) { raise NotImplementedError }
|
744
|
+
|
745
|
+
def stub_service_request
|
746
|
+
raise NotImplementedError, "stub the request without setting the return response"
|
747
|
+
end
|
748
|
+
|
749
|
+
def handled_responses
|
750
|
+
raise NotImplementedError, "Something like: [[:and_return, {status: 401}], [:and_raise, SocketError.new('hi')]]"
|
751
|
+
end
|
752
|
+
|
753
|
+
def unhandled_response
|
754
|
+
raise NotImplementedError, "Something like: [:and_return, {status: 500}]"
|
755
|
+
end
|
756
|
+
|
757
|
+
def insert_required_data_callback
|
758
|
+
# See backfiller example
|
759
|
+
return ->(*) { return }
|
760
|
+
end
|
761
|
+
|
762
|
+
before(:each) do
|
763
|
+
sint.organization.prepare_database_connections
|
764
|
+
end
|
765
|
+
|
766
|
+
after(:each) do
|
767
|
+
sint.organization.remove_related_database
|
768
|
+
end
|
769
|
+
|
770
|
+
it "dispatches an alert and returns true for handled errors (using default alert)" do
|
771
|
+
create_all_dependencies(sint)
|
772
|
+
setup_dependencies(sint, insert_required_data_callback)
|
773
|
+
Webhookdb::Fixtures.organization_membership.org(sint.organization).verified.admin.create
|
774
|
+
req = stub_service_request
|
775
|
+
handled_responses.each { |(m, arg)| req.send(m, arg) }
|
776
|
+
handled_responses.count.times do
|
777
|
+
backfill(sint)
|
778
|
+
end
|
779
|
+
expect(req).to have_been_made.times(handled_responses.count)
|
780
|
+
expect(Webhookdb::Message::Delivery.all).to contain_exactly(
|
781
|
+
have_attributes(template: template_name),
|
782
|
+
)
|
783
|
+
end
|
784
|
+
|
785
|
+
it "dispatches an alert and returns true for handled errors (using error handler)" do
|
786
|
+
create_all_dependencies(sint)
|
787
|
+
setup_dependencies(sint, insert_required_data_callback)
|
788
|
+
Webhookdb::Fixtures.organization_membership.org(sint.organization).verified.admin.create
|
789
|
+
req = stub_service_request
|
790
|
+
eh = Webhookdb::Fixtures.organization_error_handler(organization: sint.organization).create
|
791
|
+
error_handle_req = stub_request(:post, eh.url).and_return(status: 204)
|
792
|
+
handled_responses.each { |(m, arg)| req.send(m, arg) }
|
793
|
+
handled_responses.count.times do
|
794
|
+
backfill(sint)
|
795
|
+
end
|
796
|
+
expect(req).to have_been_made.times(handled_responses.count)
|
797
|
+
expect(error_handle_req).to have_been_made.times(handled_responses.count)
|
798
|
+
end
|
799
|
+
|
800
|
+
it "does not dispatch an alert, and raises the original error, if unhandled" do
|
801
|
+
create_all_dependencies(sint)
|
802
|
+
setup_dependencies(sint, insert_required_data_callback)
|
803
|
+
Webhookdb::Fixtures.organization_membership.org(sint.organization).verified.admin.create
|
804
|
+
req = stub_service_request.send(*unhandled_response)
|
805
|
+
expect { backfill(sint) }.to raise_error(Amigo::Retry::OrDie)
|
806
|
+
expect(req).to have_been_made
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
710
810
|
RSpec.shared_examples "a replicator that verifies backfill secrets" do
|
711
811
|
let(:correct_creds_sint) { raise NotImplementedError, "what sint should we use to test correct creds?" }
|
712
812
|
let(:incorrect_creds_sint) { raise NotImplementedError, "what sint should we use to test incorrect creds?" }
|
@@ -748,19 +848,21 @@ RSpec.shared_examples "a replicator that verifies backfill secrets" do
|
|
748
848
|
end
|
749
849
|
end
|
750
850
|
|
751
|
-
RSpec.shared_examples "a replicator with a custom backfill not supported message" do
|
851
|
+
RSpec.shared_examples "a replicator with a custom backfill not supported message" do
|
852
|
+
let(:service_name) { described_class.descriptor.name }
|
752
853
|
it "has a custom message" do
|
753
|
-
sint = Webhookdb::Fixtures.service_integration.create(service_name:
|
854
|
+
sint = Webhookdb::Fixtures.service_integration.create(service_name:)
|
754
855
|
expect(sint.replicator.backfill_not_supported_message).to_not include("You may be looking for one of the following")
|
755
856
|
end
|
756
857
|
end
|
757
858
|
|
758
|
-
RSpec.shared_examples "a backfill replicator that marks missing rows as deleted" do
|
859
|
+
RSpec.shared_examples "a backfill replicator that marks missing rows as deleted" do
|
860
|
+
let(:service_name) { described_class.descriptor.name }
|
759
861
|
let(:deleted_column_name) { raise NotImplementedError }
|
760
862
|
let(:api_url) { "https://fake-url.com" }
|
761
863
|
let(:sint) do
|
762
864
|
Webhookdb::Fixtures.service_integration.create(
|
763
|
-
service_name
|
865
|
+
service_name:,
|
764
866
|
backfill_key: "bfkey",
|
765
867
|
backfill_secret: "bfsek",
|
766
868
|
api_url:,
|
@@ -813,11 +915,12 @@ RSpec.shared_examples "a backfill replicator that marks missing rows as deleted"
|
|
813
915
|
end
|
814
916
|
end
|
815
917
|
|
816
|
-
RSpec.shared_examples "a replicator that ignores HTTP errors during backfill" do
|
918
|
+
RSpec.shared_examples "a replicator that ignores HTTP errors during backfill" do
|
919
|
+
let(:service_name) { described_class.descriptor.name }
|
817
920
|
let(:api_url) { "https://fake-url.com" }
|
818
921
|
let(:sint) do
|
819
922
|
Webhookdb::Fixtures.service_integration.create(
|
820
|
-
service_name
|
923
|
+
service_name:,
|
821
924
|
backfill_key: "bfkey",
|
822
925
|
backfill_secret: "bfsek",
|
823
926
|
api_url:,
|
@@ -856,8 +959,9 @@ RSpec.shared_examples "a replicator that ignores HTTP errors during backfill" do
|
|
856
959
|
end
|
857
960
|
end
|
858
961
|
|
859
|
-
RSpec.shared_examples "a replicator backfilling against the table of its dependency" do
|
860
|
-
let(:
|
962
|
+
RSpec.shared_examples "a replicator backfilling against the table of its dependency" do
|
963
|
+
let(:service_name) { described_class.descriptor.name }
|
964
|
+
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
861
965
|
let(:svc) { Webhookdb::Replicator.create(sint) }
|
862
966
|
let(:dep_svc) { @dep_svc }
|
863
967
|
let(:external_id_col) { raise NotImplementedError }
|
@@ -913,3 +1017,74 @@ RSpec.shared_examples "a replicator backfilling against the table of its depende
|
|
913
1017
|
expect(svc.readonly_dataset(&:all)).to have_length(3)
|
914
1018
|
end
|
915
1019
|
end
|
1020
|
+
|
1021
|
+
# These shared examples test partitioning
|
1022
|
+
|
1023
|
+
RSpec.shared_examples "a replicator that supports hash partitioning" do
|
1024
|
+
let(:service_name) { described_class.descriptor.name }
|
1025
|
+
let(:partitions) { 4 }
|
1026
|
+
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:, partition_value: partitions) }
|
1027
|
+
let(:svc) { Webhookdb::Replicator.create(sint) }
|
1028
|
+
Webhookdb::SpecHelpers::Whdb.setup_upsert_webhook_example(self)
|
1029
|
+
|
1030
|
+
before(:each) do
|
1031
|
+
sint.organization.prepare_database_connections
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
after(:each) do
|
1035
|
+
sint.organization.remove_related_database
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
def body(_i) = raise NotImplementedError
|
1039
|
+
|
1040
|
+
it "creates and uses the specified number of hash partitions" do
|
1041
|
+
expect(svc).to be_partition
|
1042
|
+
expect(svc.partitioning).to be_a(Webhookdb::DBAdapter::Partitioning)
|
1043
|
+
m = svc.create_table_modification
|
1044
|
+
expect(m.transaction_statements).to include(
|
1045
|
+
match(/CREATE TABLE .*_0 PARTITION OF .* FOR VALUES WITH \(MODULUS \d, REMAINDER 0\)/),
|
1046
|
+
match(/CREATE TABLE .*_1 PARTITION OF .* FOR VALUES WITH \(MODULUS \d, REMAINDER 1\)/),
|
1047
|
+
match(/CREATE TABLE .*_2 PARTITION OF .* FOR VALUES WITH \(MODULUS \d, REMAINDER 2\)/),
|
1048
|
+
match(/CREATE TABLE .*_3 PARTITION OF .* FOR VALUES WITH \(MODULUS \d, REMAINDER 3\)/),
|
1049
|
+
)
|
1050
|
+
expect { svc.create_table }.to_not raise_error
|
1051
|
+
rowcount = partitions + 2 # Make sure we hit all the remainders and extra
|
1052
|
+
Array.new(2) do
|
1053
|
+
(0...rowcount).each do |i|
|
1054
|
+
upsert_webhook(svc, body: body(i))
|
1055
|
+
end
|
1056
|
+
end
|
1057
|
+
svc.readonly_dataset do |ds|
|
1058
|
+
expect(ds.all).to have_length(rowcount)
|
1059
|
+
end
|
1060
|
+
end
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
RSpec.shared_examples "a replicator that supports range partitioning" do
|
1064
|
+
let(:service_name) { described_class.descriptor.name }
|
1065
|
+
let(:sint) { Webhookdb::Fixtures.service_integration.create(service_name:) }
|
1066
|
+
let(:svc) { Webhookdb::Replicator.create(sint) }
|
1067
|
+
Webhookdb::SpecHelpers::Whdb.setup_upsert_webhook_example(self)
|
1068
|
+
|
1069
|
+
before(:each) do
|
1070
|
+
sint.organization.prepare_database_connections
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
after(:each) do
|
1074
|
+
sint.organization.remove_related_database
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
def body(_i) = raise NotImplementedError
|
1078
|
+
|
1079
|
+
it "prepares partitions" do
|
1080
|
+
expect(svc).to be_partition
|
1081
|
+
expect(svc.partitioning).to be_a(Webhookdb::DBAdapter::Partitioning)
|
1082
|
+
m = svc.create_table_modification
|
1083
|
+
expect(m.transaction_statements).to include(
|
1084
|
+
match(/PARTITION BY RANGE \(/),
|
1085
|
+
)
|
1086
|
+
expect { svc.create_table }.to_not raise_error
|
1087
|
+
# Awaiting implementation
|
1088
|
+
expect { upsert_webhook(svc, body: body(1)) }.to raise_error(/no partition of relation/)
|
1089
|
+
end
|
1090
|
+
end
|