Skip to content

Commit 41e18f9

Browse files
authored
CP-12376 extend webhooks partnerships (#55)
* add partnership_id to webhook_urls - add migration to make account_id OR partnership_id required, you can use either, but can and must use at least one - add PARTNERSHIP_EVENTS constant to constrain webhook firing to just template events * extract duplicated webhook retry logic for webhook jobs - 11 webhook jobs all used the same retry logic (except one file that had 12 max retries instead of 10. - remove and replace duplicated code - add retry logic for partnership templates * refactor WebhookUrls to support partnerships - add for_template method to support account/partnership templates. - for_account_id will still work for submissions - update controllers with new method - I'm not great with Arel, so I refactored since I wanted account/partnership to use a shared method. * a automatic webhook creation for new partnerships * fix rubocop violations * update spec to expect raised error instead of empty array * fix rubocop/rspec for HTTP requests in test * remove after commit partnership webhook temporarily The immediately following PR will add this `after_commit` back. We only really need the upcoming template.preferences_updated webhook event that will be in the next PR, so even though it's unlikely anyone will be testing this at the Partnership level right now, better to just remove it for the time being for a cleaner PR. * validate incoming events against WebhookUrl::EVENTS constant * safety against SQL injection This method does not accept user input, but adding this just to be safe. * add form.changes_requested to events * since we added the events constant checker, we need to make sure this event is part of the constant list
1 parent 3570b7c commit 41e18f9

27 files changed

Lines changed: 646 additions & 148 deletions

app/controllers/api/templates_clone_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def finalize_and_render_response(cloned_template)
8686
end
8787

8888
def enqueue_webhooks(template)
89-
WebhookUrls.for_account_id(template.account_id, 'template.created').each do |webhook_url|
89+
WebhookUrls.for_template(template, 'template.created').each do |webhook_url|
9090
SendTemplateCreatedWebhookRequestJob.perform_async('template_id' => template.id,
9191
'webhook_url_id' => webhook_url.id)
9292
end

app/controllers/api/templates_controller.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,9 @@ def enqueue_template_updated_webhooks
220220
end
221221

222222
def enqueue_template_webhooks(template, event_type, job_class)
223-
return if template.account_id.blank?
223+
return if template.partnership.blank? && template.account.blank?
224224

225-
WebhookUrls.for_account_id(template.account_id, event_type).each do |webhook_url|
225+
WebhookUrls.for_template(template, event_type).each do |webhook_url|
226226
job_class.perform_async('template_id' => template.id, 'webhook_url_id' => webhook_url.id)
227227
end
228228
end

app/controllers/templates_controller.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,14 +160,14 @@ def maybe_redirect_to_template(template)
160160
end
161161

162162
def enqueue_template_created_webhooks(template)
163-
WebhookUrls.for_account_id(template.account_id, 'template.created').each do |webhook_url|
163+
WebhookUrls.for_template(template, 'template.created').each do |webhook_url|
164164
SendTemplateCreatedWebhookRequestJob.perform_async('template_id' => template.id,
165165
'webhook_url_id' => webhook_url.id)
166166
end
167167
end
168168

169169
def enqueue_template_updated_webhooks(template)
170-
WebhookUrls.for_account_id(template.account_id, 'template.updated').each do |webhook_url|
170+
WebhookUrls.for_template(template, 'template.updated').each do |webhook_url|
171171
SendTemplateUpdatedWebhookRequestJob.perform_async('template_id' => template.id,
172172
'webhook_url_id' => webhook_url.id)
173173
end

app/controllers/templates_uploads_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def create_file_params_from_url
7272
end
7373

7474
def enqueue_template_created_webhooks(template)
75-
WebhookUrls.for_account_id(template.account_id, 'template.created').each do |webhook_url|
75+
WebhookUrls.for_template(template, 'template.created').each do |webhook_url|
7676
SendTemplateCreatedWebhookRequestJob.perform_async('template_id' => template.id,
7777
'webhook_url_id' => webhook_url.id)
7878
end

app/jobs/send_form_changes_requested_webhook_request_job.rb

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ class SendFormChangesRequestedWebhookRequestJob
55

66
sidekiq_options queue: :webhooks
77

8-
MAX_ATTEMPTS = 10
9-
108
def perform(params = {})
119
submitter = Submitter.find(params['submitter_id'])
1210
webhook_url = WebhookUrl.find(params['webhook_url_id'])
@@ -20,14 +18,13 @@ def perform(params = {})
2018
resp = SendWebhookRequest.call(webhook_url, event_type: 'form.changes_requested',
2119
data: Submitters::SerializeForWebhook.call(submitter))
2220

23-
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
24-
(!Docuseal.multitenant? || submitter.account.account_configs.exists?(key: :plan))
25-
SendFormChangesRequestedWebhookRequestJob.perform_in((2**attempt).minutes, {
26-
'submitter_id' => submitter.id,
27-
'webhook_url_id' => webhook_url.id,
28-
'attempt' => attempt + 1,
29-
'last_status' => resp&.status.to_i
30-
})
31-
end
21+
return unless WebhookRetryLogic.should_retry?(response: resp, attempt: attempt, record: submitter)
22+
23+
SendFormChangesRequestedWebhookRequestJob.perform_in((2**attempt).minutes, {
24+
'submitter_id' => submitter.id,
25+
'webhook_url_id' => webhook_url.id,
26+
'attempt' => attempt + 1,
27+
'last_status' => resp&.status.to_i
28+
})
3229
end
3330
end

app/jobs/send_form_completed_webhook_request_job.rb

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ class SendFormCompletedWebhookRequestJob
55

66
sidekiq_options queue: :webhooks
77

8-
MAX_ATTEMPTS = 12
9-
108
def perform(params = {})
119
submitter = Submitter.find(params['submitter_id'])
1210
webhook_url = WebhookUrl.find(params['webhook_url_id'])
@@ -28,14 +26,13 @@ def perform(params = {})
2826
resp = SendWebhookRequest.call(webhook_url, event_type: 'form.completed',
2927
data: webhook_data)
3028

31-
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
32-
(!Docuseal.multitenant? || submitter.account.account_configs.exists?(key: :plan))
33-
SendFormCompletedWebhookRequestJob.perform_in((2**attempt).minutes, {
34-
**params,
35-
'attempt' => attempt + 1,
36-
'last_status' => resp&.status.to_i
37-
})
38-
end
29+
return unless WebhookRetryLogic.should_retry?(response: resp, attempt: attempt, record: submitter)
30+
31+
SendFormCompletedWebhookRequestJob.perform_in((2**attempt).minutes, {
32+
**params,
33+
'attempt' => attempt + 1,
34+
'last_status' => resp&.status.to_i
35+
})
3936
end
4037

4138
private

app/jobs/send_form_declined_webhook_request_job.rb

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ class SendFormDeclinedWebhookRequestJob
55

66
sidekiq_options queue: :webhooks
77

8-
MAX_ATTEMPTS = 10
9-
108
def perform(params = {})
119
submitter = Submitter.find(params['submitter_id'])
1210
webhook_url = WebhookUrl.find(params['webhook_url_id'])
@@ -20,14 +18,13 @@ def perform(params = {})
2018
resp = SendWebhookRequest.call(webhook_url, event_type: 'form.declined',
2119
data: Submitters::SerializeForWebhook.call(submitter))
2220

23-
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
24-
(!Docuseal.multitenant? || submitter.account.account_configs.exists?(key: :plan))
25-
SendFormDeclinedWebhookRequestJob.perform_in((2**attempt).minutes, {
26-
'submitter_id' => submitter.id,
27-
'webhook_url_id' => webhook_url.id,
28-
'attempt' => attempt + 1,
29-
'last_status' => resp&.status.to_i
30-
})
31-
end
21+
return unless WebhookRetryLogic.should_retry?(response: resp, attempt: attempt, record: submitter)
22+
23+
SendFormDeclinedWebhookRequestJob.perform_in((2**attempt).minutes, {
24+
'submitter_id' => submitter.id,
25+
'webhook_url_id' => webhook_url.id,
26+
'attempt' => attempt + 1,
27+
'last_status' => resp&.status.to_i
28+
})
3229
end
3330
end

app/jobs/send_form_started_webhook_request_job.rb

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ class SendFormStartedWebhookRequestJob
55

66
sidekiq_options queue: :webhooks
77

8-
MAX_ATTEMPTS = 10
9-
108
def perform(params = {})
119
submitter = Submitter.find(params['submitter_id'])
1210
webhook_url = WebhookUrl.find(params['webhook_url_id'])
@@ -20,14 +18,13 @@ def perform(params = {})
2018
resp = SendWebhookRequest.call(webhook_url, event_type: 'form.started',
2119
data: Submitters::SerializeForWebhook.call(submitter))
2220

23-
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
24-
(!Docuseal.multitenant? || submitter.account.account_configs.exists?(key: :plan))
25-
SendFormStartedWebhookRequestJob.perform_in((2**attempt).minutes, {
26-
'submitter_id' => submitter.id,
27-
'webhook_url_id' => webhook_url.id,
28-
'attempt' => attempt + 1,
29-
'last_status' => resp&.status.to_i
30-
})
31-
end
21+
return unless WebhookRetryLogic.should_retry?(response: resp, attempt: attempt, record: submitter)
22+
23+
SendFormStartedWebhookRequestJob.perform_in((2**attempt).minutes, {
24+
'submitter_id' => submitter.id,
25+
'webhook_url_id' => webhook_url.id,
26+
'attempt' => attempt + 1,
27+
'last_status' => resp&.status.to_i
28+
})
3229
end
3330
end

app/jobs/send_form_viewed_webhook_request_job.rb

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ class SendFormViewedWebhookRequestJob
55

66
sidekiq_options queue: :webhooks
77

8-
MAX_ATTEMPTS = 10
9-
108
def perform(params = {})
119
submitter = Submitter.find(params['submitter_id'])
1210
webhook_url = WebhookUrl.find(params['webhook_url_id'])
@@ -20,14 +18,13 @@ def perform(params = {})
2018
resp = SendWebhookRequest.call(webhook_url, event_type: 'form.viewed',
2119
data: Submitters::SerializeForWebhook.call(submitter))
2220

23-
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
24-
(!Docuseal.multitenant? || submitter.account.account_configs.exists?(key: :plan))
25-
SendFormViewedWebhookRequestJob.perform_in((2**attempt).minutes, {
26-
'submitter_id' => submitter.id,
27-
'webhook_url_id' => webhook_url.id,
28-
'attempt' => attempt + 1,
29-
'last_status' => resp&.status.to_i
30-
})
31-
end
21+
return unless WebhookRetryLogic.should_retry?(response: resp, attempt: attempt, record: submitter)
22+
23+
SendFormViewedWebhookRequestJob.perform_in((2**attempt).minutes, {
24+
'submitter_id' => submitter.id,
25+
'webhook_url_id' => webhook_url.id,
26+
'attempt' => attempt + 1,
27+
'last_status' => resp&.status.to_i
28+
})
3229
end
3330
end

app/jobs/send_submission_archived_webhook_request_job.rb

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ class SendSubmissionArchivedWebhookRequestJob
55

66
sidekiq_options queue: :webhooks
77

8-
MAX_ATTEMPTS = 10
9-
108
def perform(params = {})
119
submission = Submission.find(params['submission_id'])
1210
webhook_url = WebhookUrl.find(params['webhook_url_id'])
@@ -18,14 +16,13 @@ def perform(params = {})
1816
resp = SendWebhookRequest.call(webhook_url, event_type: 'submission.archived',
1917
data: submission.as_json(only: %i[id archived_at]))
2018

21-
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
22-
(!Docuseal.multitenant? || submission.account.account_configs.exists?(key: :plan))
23-
SendSubmissionArchivedWebhookRequestJob.perform_in((2**attempt).minutes, {
24-
'submission_id' => submission.id,
25-
'webhook_url_id' => webhook_url.id,
26-
'attempt' => attempt + 1,
27-
'last_status' => resp&.status.to_i
28-
})
29-
end
19+
return unless WebhookRetryLogic.should_retry?(response: resp, attempt: attempt, record: submission)
20+
21+
SendSubmissionArchivedWebhookRequestJob.perform_in((2**attempt).minutes, {
22+
'submission_id' => submission.id,
23+
'webhook_url_id' => webhook_url.id,
24+
'attempt' => attempt + 1,
25+
'last_status' => resp&.status.to_i
26+
})
3027
end
3128
end

0 commit comments

Comments
 (0)