diff --git a/app/controllers/api/submissions_controller.rb b/app/controllers/api/submissions_controller.rb index 49028684..b96aa420 100644 --- a/app/controllers/api/submissions_controller.rb +++ b/app/controllers/api/submissions_controller.rb @@ -102,6 +102,8 @@ module Api def destroy @submission.update!(archived_at: Time.current) + SendSubmissionArchivedWebhookRequestJob.perform_later(@submission) + render json: @submission.as_json(only: %i[id], methods: %i[archived_at]) end diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 69f3aa49..b68f32ae 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -50,6 +50,8 @@ class SubmissionsController < ApplicationController def destroy @submission.update!(archived_at: Time.current) + SendSubmissionArchivedWebhookRequestJob.perform_later(@submission) + redirect_back(fallback_location: template_path(@submission.template), notice: 'Submission has been archived') end diff --git a/app/controllers/webhook_preferences_controller.rb b/app/controllers/webhook_preferences_controller.rb new file mode 100644 index 00000000..4d9c7353 --- /dev/null +++ b/app/controllers/webhook_preferences_controller.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class WebhookPreferencesController < ApplicationController + EVENTS = %w[ + form.viewed + form.started + form.completed + submission.archived + ].freeze + + before_action :load_account_config + authorize_resource :account_config, parent: false + + def create + @account_config.value[account_config_params[:event]] = account_config_params[:value] == '1' + + @account_config.save! + + head :ok + end + + private + + def load_account_config + @account_config = + current_account.account_configs.find_or_initialize_by(key: AccountConfig::WEBHOOK_PREFERENCES_KEY) + @account_config.value ||= {} + end + + def account_config_params + params.permit(:event, :value) + end +end diff --git a/app/jobs/process_submitter_completion_job.rb b/app/jobs/process_submitter_completion_job.rb index 157f8287..cf22112a 100644 --- a/app/jobs/process_submitter_completion_job.rb +++ b/app/jobs/process_submitter_completion_job.rb @@ -16,7 +16,7 @@ class ProcessSubmitterCompletionJob < ApplicationJob enqueue_completed_emails(submitter) end - return if Accounts.load_webhook_configs(submitter.account).blank? + return if Accounts.load_webhook_url(submitter.account).blank? SendFormCompletedWebhookRequestJob.perform_later(submitter) end diff --git a/app/jobs/send_form_completed_webhook_request_job.rb b/app/jobs/send_form_completed_webhook_request_job.rb index 134cefb9..7886248a 100644 --- a/app/jobs/send_form_completed_webhook_request_job.rb +++ b/app/jobs/send_form_completed_webhook_request_job.rb @@ -7,16 +7,20 @@ class SendFormCompletedWebhookRequestJob < ApplicationJob def perform(submitter, params = {}) attempt = params[:attempt].to_i - config = Accounts.load_webhook_configs(submitter.submission.account) + url = Accounts.load_webhook_url(submitter.submission.account) - return if config.blank? || config.value.blank? + return if url.blank? + + preferences = Accounts.load_webhook_preferences(submitter.submission.account) + + return if preferences['form.completed'] == false Submissions::EnsureResultGenerated.call(submitter) ActiveStorage::Current.url_options = Docuseal.default_url_options resp = begin - Faraday.post(config.value, + Faraday.post(url, { event_type: 'form.completed', timestamp: Time.current, diff --git a/app/jobs/send_form_started_webhook_request_job.rb b/app/jobs/send_form_started_webhook_request_job.rb index bc0168f4..5536eab9 100644 --- a/app/jobs/send_form_started_webhook_request_job.rb +++ b/app/jobs/send_form_started_webhook_request_job.rb @@ -7,14 +7,18 @@ class SendFormStartedWebhookRequestJob < ApplicationJob def perform(submitter, params = {}) attempt = params[:attempt].to_i - config = Accounts.load_webhook_configs(submitter.submission.account) + url = Accounts.load_webhook_url(submitter.submission.account) - return if config.blank? || config.value.blank? + return if url.blank? + + preferences = Accounts.load_webhook_preferences(submitter.submission.account) + + return if preferences['form.started'] == false ActiveStorage::Current.url_options = Docuseal.default_url_options resp = begin - Faraday.post(config.value, + Faraday.post(url, { event_type: 'form.started', timestamp: Time.current, diff --git a/app/jobs/send_form_viewed_webhook_request_job.rb b/app/jobs/send_form_viewed_webhook_request_job.rb index 31360c5c..18b3da82 100644 --- a/app/jobs/send_form_viewed_webhook_request_job.rb +++ b/app/jobs/send_form_viewed_webhook_request_job.rb @@ -7,14 +7,18 @@ class SendFormViewedWebhookRequestJob < ApplicationJob def perform(submitter, params = {}) attempt = params[:attempt].to_i - config = Accounts.load_webhook_configs(submitter.submission.account) + url = Accounts.load_webhook_url(submitter.submission.account) - return if config.blank? || config.value.blank? + return if url.blank? + + preferences = Accounts.load_webhook_preferences(submitter.submission.account) + + return if preferences['form.viewed'] == false ActiveStorage::Current.url_options = Docuseal.default_url_options resp = begin - Faraday.post(config.value, + Faraday.post(url, { event_type: 'form.viewed', timestamp: Time.current, diff --git a/app/jobs/send_submission_archived_webhook_request_job.rb b/app/jobs/send_submission_archived_webhook_request_job.rb new file mode 100644 index 00000000..bf05dcaf --- /dev/null +++ b/app/jobs/send_submission_archived_webhook_request_job.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +class SendSubmissionArchivedWebhookRequestJob < ApplicationJob + USER_AGENT = 'DocuSeal.co Webhook' + + MAX_ATTEMPTS = 10 + + def perform(submission, params = {}) + attempt = params[:attempt].to_i + url = Accounts.load_webhook_url(submission.account) + + return if url.blank? + + preferences = Accounts.load_webhook_preferences(submission.account) + + return if preferences['submission.archived'].blank? + + resp = begin + Faraday.post(url, + { + event_type: 'submission.archived', + timestamp: Time.current, + data: submission.as_json(only: %i[id archived_at]) + }.to_json, + 'Content-Type' => 'application/json', + 'User-Agent' => USER_AGENT) + rescue Faraday::Error + nil + end + + if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS && + (!Docuseal.multitenant? || submitter.account.account_configs.exists?(key: :plan)) + SendSubmissionArchivedWebhookRequestJob.set(wait: (2**attempt).minutes) + .perform_later(submitter, { + attempt: attempt + 1, + last_status: resp&.status.to_i + }) + end + end +end diff --git a/app/models/account_config.rb b/app/models/account_config.rb index 27b1058e..51b76b21 100644 --- a/app/models/account_config.rb +++ b/app/models/account_config.rb @@ -33,6 +33,7 @@ class AccountConfig < ApplicationRecord FORM_COMPLETED_MESSAGE_KEY = 'form_completed_message' FORM_WITH_CONFETTI_KEY = 'form_with_confetti' ESIGNING_PREFERENCE_KEY = 'esigning_preference' + WEBHOOK_PREFERENCES_KEY = 'webhook_preferences' DEFAULT_VALUES = { SUBMITTER_INVITATION_EMAIL_KEY => { diff --git a/app/views/webhook_settings/show.html.erb b/app/views/webhook_settings/show.html.erb index 34c76787..c834d28d 100644 --- a/app/views/webhook_settings/show.html.erb +++ b/app/views/webhook_settings/show.html.erb @@ -14,6 +14,23 @@ <%= f.button button_title(title: 'Save', disabled_with: 'Saving'), class: 'base-button w-full md:w-32' %> <% end %> + <% preference = current_account.account_configs.find_by(key: AccountConfig::WEBHOOK_PREFERENCES_KEY)&.value || {} %> +
+ <% WebhookPreferencesController::EVENTS.each do |event| %> + <%= form_for '', url: webhook_preferences_path, method: :post do |f| %> + <%= f.hidden_field :event, value: event %> + <% uuid = SecureRandom.uuid %> +
+ +
+ <% end %> + <% end %> +
<% submitter = current_account.submitters.where.not(completed_at: nil).order(:id).last %> diff --git a/config/routes.rb b/config/routes.rb index 76bf5399..93aa9d17 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -59,6 +59,7 @@ Rails.application.routes.draw do resources :testing_api_settings, only: %i[index] resources :submitters_autocomplete, only: %i[index] resources :template_folders_autocomplete, only: %i[index] + resources :webhook_preferences, only: %i[create] resource :templates_upload, only: %i[create] authenticated do resource :templates_upload, only: %i[show], path: 'new' diff --git a/lib/accounts.rb b/lib/accounts.rb index 9e6da33e..7b8fb8cb 100644 --- a/lib/accounts.rb +++ b/lib/accounts.rb @@ -67,14 +67,24 @@ module Accounts new_template end - def load_webhook_configs(account) + def load_webhook_url(account) configs = account.encrypted_configs.find_by(key: EncryptedConfig::WEBHOOK_URL_KEY) unless Docuseal.multitenant? configs ||= Account.order(:id).first.encrypted_configs.find_by(key: EncryptedConfig::WEBHOOK_URL_KEY) end - configs + configs&.value.presence + end + + def load_webhook_preferences(account) + configs = account.account_configs.find_by(key: AccountConfig::WEBHOOK_PREFERENCES_KEY) + + unless Docuseal.multitenant? + configs ||= Account.order(:id).first.account_configs.find_by(key: AccountConfig::WEBHOOK_PREFERENCES_KEY) + end + + configs&.value.presence || {} end def load_signing_pkcs(account)