use webhook urls instead of encrypted configs

This commit is contained in:
Alex Turchyn
2024-10-28 20:39:09 +02:00
committed by Pete Matsyburka
parent f669045362
commit 84edb6dbda
50 changed files with 1350 additions and 269 deletions
+8
View File
@@ -0,0 +1,8 @@
# frozen_string_literal: true
FactoryBot.define do
factory :webhook_url do
account
url { Faker::Internet.url }
end
end
@@ -0,0 +1,108 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe SendFormCompletedWebhookRequestJob do
let(:account) { create(:account) }
let(:user) { create(:user, account:) }
let(:template) { create(:template, account:, author: user) }
let(:submission) { create(:submission, template:, created_by_user: user) }
let(:submitter) do
create(:submitter, submission:, uuid: template.submitters.first['uuid'], completed_at: Time.current)
end
let(:webhook_url) { create(:webhook_url, account:, events: ['form.completed']) }
before do
create(:encrypted_config, key: EncryptedConfig::ESIGN_CERTS_KEY,
value: GenerateCertificate.call.transform_values(&:to_pem))
end
describe '#perform' do
before do
stub_request(:post, webhook_url.url).to_return(status: 200)
end
it 'sends a webhook request' do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'form.completed',
'timestamp' => Time.current,
'data' => Submitters::SerializeForWebhook.call(submitter.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook'
}
).once
end
it 'sends a webhook request with the secret' do
webhook_url.update(secret: { 'X-Secret-Header' => 'secret_value' })
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'form.completed',
'timestamp' => Time.current,
'data' => Submitters::SerializeForWebhook.call(submitter.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook',
'X-Secret-Header' => 'secret_value'
}
).once
end
it "doesn't send a webhook request if the submitter doesn't exist" do
expect do
described_class.new.perform('submitter_id' => 100_500, 'webhook_url_id' => webhook_url.id)
end.to raise_error ActiveRecord::RecordNotFound
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the event is not in the webhook's events" do
webhook_url.update!(events: ['form.declined'])
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the webhook doesn't exist" do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => 100_500)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it 'sends again if the response status is 400 or higher' do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
end.to change(described_class.jobs, :size).by(1)
expect(WebMock).to have_requested(:post, webhook_url.url).once
args = described_class.jobs.last['args'].first
expect(args['attempt']).to eq(1)
expect(args['last_status']).to eq(401)
expect(args['webhook_url_id']).to eq(webhook_url.id)
expect(args['submitter_id']).to eq(submitter.id)
end
it "doesn't send again if the max attempts is reached" do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id, 'attempt' => 11)
end.not_to change(described_class.jobs, :size)
expect(WebMock).to have_requested(:post, webhook_url.url).once
end
end
end
@@ -0,0 +1,108 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe SendFormDeclinedWebhookRequestJob do
let(:account) { create(:account) }
let(:user) { create(:user, account:) }
let(:template) { create(:template, account:, author: user) }
let(:submission) { create(:submission, template:, created_by_user: user) }
let(:submitter) do
create(:submitter, submission:, uuid: template.submitters.first['uuid'], completed_at: Time.current)
end
let(:webhook_url) { create(:webhook_url, account:, events: ['form.declined']) }
before do
create(:encrypted_config, key: EncryptedConfig::ESIGN_CERTS_KEY,
value: GenerateCertificate.call.transform_values(&:to_pem))
end
describe '#perform' do
before do
stub_request(:post, webhook_url.url).to_return(status: 200)
end
it 'sends a webhook request' do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'form.declined',
'timestamp' => Time.current,
'data' => Submitters::SerializeForWebhook.call(submitter.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook'
}
).once
end
it 'sends a webhook request with the secret' do
webhook_url.update(secret: { 'X-Secret-Header' => 'secret_value' })
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'form.declined',
'timestamp' => Time.current,
'data' => Submitters::SerializeForWebhook.call(submitter.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook',
'X-Secret-Header' => 'secret_value'
}
).once
end
it "doesn't send a webhook request if the submitter doesn't exist" do
expect do
described_class.new.perform('submitter_id' => 100_500, 'webhook_url_id' => webhook_url.id)
end.to raise_error ActiveRecord::RecordNotFound
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the event is not in the webhook's events" do
webhook_url.update!(events: ['form.completed'])
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the webhook doesn't exist" do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => 100_500)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it 'sends again if the response status is 400 or higher' do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
end.to change(described_class.jobs, :size).by(1)
expect(WebMock).to have_requested(:post, webhook_url.url).once
args = described_class.jobs.last['args'].first
expect(args['attempt']).to eq(1)
expect(args['last_status']).to eq(401)
expect(args['webhook_url_id']).to eq(webhook_url.id)
expect(args['submitter_id']).to eq(submitter.id)
end
it "doesn't send again if the max attempts is reached" do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id, 'attempt' => 11)
end.not_to change(described_class.jobs, :size)
expect(WebMock).to have_requested(:post, webhook_url.url).once
end
end
end
@@ -0,0 +1,108 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe SendFormStartedWebhookRequestJob do
let(:account) { create(:account) }
let(:user) { create(:user, account:) }
let(:template) { create(:template, account:, author: user) }
let(:submission) { create(:submission, template:, created_by_user: user) }
let(:submitter) do
create(:submitter, submission:, uuid: template.submitters.first['uuid'], completed_at: Time.current)
end
let(:webhook_url) { create(:webhook_url, account:, events: ['form.started']) }
before do
create(:encrypted_config, key: EncryptedConfig::ESIGN_CERTS_KEY,
value: GenerateCertificate.call.transform_values(&:to_pem))
end
describe '#perform' do
before do
stub_request(:post, webhook_url.url).to_return(status: 200)
end
it 'sends a webhook request' do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'form.started',
'timestamp' => Time.current,
'data' => Submitters::SerializeForWebhook.call(submitter.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook'
}
).once
end
it 'sends a webhook request with the secret' do
webhook_url.update(secret: { 'X-Secret-Header' => 'secret_value' })
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'form.started',
'timestamp' => Time.current,
'data' => Submitters::SerializeForWebhook.call(submitter.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook',
'X-Secret-Header' => 'secret_value'
}
).once
end
it "doesn't send a webhook request if the submitter doesn't exist" do
expect do
described_class.new.perform('submitter_id' => 100_500, 'webhook_url_id' => webhook_url.id)
end.to raise_error ActiveRecord::RecordNotFound
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the event is not in the webhook's events" do
webhook_url.update!(events: ['form.declined'])
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the webhook doesn't exist" do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => 100_500)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it 'sends again if the response status is 400 or higher' do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
end.to change(described_class.jobs, :size).by(1)
expect(WebMock).to have_requested(:post, webhook_url.url).once
args = described_class.jobs.last['args'].first
expect(args['attempt']).to eq(1)
expect(args['last_status']).to eq(401)
expect(args['webhook_url_id']).to eq(webhook_url.id)
expect(args['submitter_id']).to eq(submitter.id)
end
it "doesn't send again if the max attempts is reached" do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id, 'attempt' => 11)
end.not_to change(described_class.jobs, :size)
expect(WebMock).to have_requested(:post, webhook_url.url).once
end
end
end
@@ -0,0 +1,108 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe SendFormViewedWebhookRequestJob do
let(:account) { create(:account) }
let(:user) { create(:user, account:) }
let(:template) { create(:template, account:, author: user) }
let(:submission) { create(:submission, template:, created_by_user: user) }
let(:submitter) do
create(:submitter, submission:, uuid: template.submitters.first['uuid'], completed_at: Time.current)
end
let(:webhook_url) { create(:webhook_url, account:, events: ['form.viewed']) }
before do
create(:encrypted_config, key: EncryptedConfig::ESIGN_CERTS_KEY,
value: GenerateCertificate.call.transform_values(&:to_pem))
end
describe '#perform' do
before do
stub_request(:post, webhook_url.url).to_return(status: 200)
end
it 'sends a webhook request' do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'form.viewed',
'timestamp' => Time.current,
'data' => Submitters::SerializeForWebhook.call(submitter.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook'
}
).once
end
it 'sends a webhook request with the secret' do
webhook_url.update(secret: { 'X-Secret-Header' => 'secret_value' })
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'form.viewed',
'timestamp' => Time.current,
'data' => Submitters::SerializeForWebhook.call(submitter.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook',
'X-Secret-Header' => 'secret_value'
}
).once
end
it "doesn't send a webhook request if the submitter doesn't exist" do
expect do
described_class.new.perform('submitter_id' => 100_500, 'webhook_url_id' => webhook_url.id)
end.to raise_error ActiveRecord::RecordNotFound
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the event is not in the webhook's events" do
webhook_url.update!(events: ['form.started'])
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the webhook doesn't exist" do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => 100_500)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it 'sends again if the response status is 400 or higher' do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id)
end.to change(described_class.jobs, :size).by(1)
expect(WebMock).to have_requested(:post, webhook_url.url).once
args = described_class.jobs.last['args'].first
expect(args['attempt']).to eq(1)
expect(args['last_status']).to eq(401)
expect(args['webhook_url_id']).to eq(webhook_url.id)
expect(args['submitter_id']).to eq(submitter.id)
end
it "doesn't send again if the max attempts is reached" do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submitter_id' => submitter.id, 'webhook_url_id' => webhook_url.id, 'attempt' => 11)
end.not_to change(described_class.jobs, :size)
expect(WebMock).to have_requested(:post, webhook_url.url).once
end
end
end
@@ -0,0 +1,106 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe SendSubmissionArchivedWebhookRequestJob do
let(:account) { create(:account) }
let(:user) { create(:user, account:) }
let(:template) { create(:template, account:, author: user) }
let(:submission) { create(:submission, template:, created_by_user: user) }
let(:webhook_url) { create(:webhook_url, account:, events: ['submission.archived']) }
before do
create(:encrypted_config, key: EncryptedConfig::ESIGN_CERTS_KEY,
value: GenerateCertificate.call.transform_values(&:to_pem))
end
describe '#perform' do
before do
stub_request(:post, webhook_url.url).to_return(status: 200)
end
it 'sends a webhook request' do
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'submission.archived',
'timestamp' => Time.current,
'data' => submission.reload.as_json(only: %i[id archived_at])
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook'
}
).once
end
it 'sends a webhook request with the secret' do
webhook_url.update(secret: { 'X-Secret-Header' => 'secret_value' })
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'submission.archived',
'timestamp' => Time.current,
'data' => submission.reload.as_json(only: %i[id archived_at])
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook',
'X-Secret-Header' => 'secret_value'
}
).once
end
it "doesn't send a webhook request if the submission doesn't exist" do
expect do
described_class.new.perform('submission_id' => 100_500, 'webhook_url_id' => webhook_url.id)
end.to raise_error ActiveRecord::RecordNotFound
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the event is not in the webhook's events" do
webhook_url.update!(events: ['submission.created'])
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the webhook doesn't exist" do
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => 100_500)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it 'sends again if the response status is 400 or higher' do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id)
end.to change(described_class.jobs, :size).by(1)
expect(WebMock).to have_requested(:post, webhook_url.url).once
args = described_class.jobs.last['args'].first
expect(args['attempt']).to eq(1)
expect(args['last_status']).to eq(401)
expect(args['webhook_url_id']).to eq(webhook_url.id)
expect(args['submission_id']).to eq(submission.id)
end
it "doesn't send again if the max attempts is reached" do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id,
'attempt' => 11)
end.not_to change(described_class.jobs, :size)
expect(WebMock).to have_requested(:post, webhook_url.url).once
end
end
end
@@ -0,0 +1,106 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe SendSubmissionCompletedWebhookRequestJob do
let(:account) { create(:account) }
let(:user) { create(:user, account:) }
let(:template) { create(:template, account:, author: user) }
let(:submission) { create(:submission, :with_submitters, template:, created_by_user: user) }
let(:webhook_url) { create(:webhook_url, account:, events: ['submission.completed']) }
before do
create(:encrypted_config, key: EncryptedConfig::ESIGN_CERTS_KEY,
value: GenerateCertificate.call.transform_values(&:to_pem))
end
describe '#perform' do
before do
stub_request(:post, webhook_url.url).to_return(status: 200)
end
it 'sends a webhook request' do
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'submission.completed',
'timestamp' => Time.current,
'data' => Submissions::SerializeForApi.call(submission.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook'
}
).once
end
it 'sends a webhook request with the secret' do
webhook_url.update(secret: { 'X-Secret-Header' => 'secret_value' })
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'submission.completed',
'timestamp' => Time.current,
'data' => Submissions::SerializeForApi.call(submission.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook',
'X-Secret-Header' => 'secret_value'
}
).once
end
it "doesn't send a webhook request if the submission doesn't exist" do
expect do
described_class.new.perform('submission_id' => 100_500, 'webhook_url_id' => webhook_url.id)
end.to raise_error ActiveRecord::RecordNotFound
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the event is not in the webhook's events" do
webhook_url.update!(events: ['submission.archived'])
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the webhook doesn't exist" do
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => 100_500)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it 'sends again if the response status is 400 or higher' do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id)
end.to change(described_class.jobs, :size).by(1)
expect(WebMock).to have_requested(:post, webhook_url.url).once
args = described_class.jobs.last['args'].first
expect(args['attempt']).to eq(1)
expect(args['last_status']).to eq(401)
expect(args['webhook_url_id']).to eq(webhook_url.id)
expect(args['submission_id']).to eq(submission.id)
end
it "doesn't send again if the max attempts is reached" do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id,
'attempt' => 11)
end.not_to change(described_class.jobs, :size)
expect(WebMock).to have_requested(:post, webhook_url.url).once
end
end
end
@@ -0,0 +1,106 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe SendSubmissionCreatedWebhookRequestJob do
let(:account) { create(:account) }
let(:user) { create(:user, account:) }
let(:template) { create(:template, account:, author: user) }
let(:submission) { create(:submission, :with_submitters, template:, created_by_user: user) }
let(:webhook_url) { create(:webhook_url, account:, events: ['submission.created']) }
before do
create(:encrypted_config, key: EncryptedConfig::ESIGN_CERTS_KEY,
value: GenerateCertificate.call.transform_values(&:to_pem))
end
describe '#perform' do
before do
stub_request(:post, webhook_url.url).to_return(status: 200)
end
it 'sends a webhook request' do
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'submission.created',
'timestamp' => Time.current,
'data' => Submissions::SerializeForApi.call(submission.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook'
}
).once
end
it 'sends a webhook request with the secret' do
webhook_url.update(secret: { 'X-Secret-Header' => 'secret_value' })
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'submission.created',
'timestamp' => Time.current,
'data' => Submissions::SerializeForApi.call(submission.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook',
'X-Secret-Header' => 'secret_value'
}
).once
end
it "doesn't send a webhook request if the submission doesn't exist" do
expect do
described_class.new.perform('submission_id' => 100_500, 'webhook_url_id' => webhook_url.id)
end.to raise_error ActiveRecord::RecordNotFound
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the event is not in the webhook's events" do
webhook_url.update!(events: ['submission.completed'])
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the webhook doesn't exist" do
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => 100_500)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it 'sends again if the response status is 400 or higher' do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id)
end.to change(described_class.jobs, :size).by(1)
expect(WebMock).to have_requested(:post, webhook_url.url).once
args = described_class.jobs.last['args'].first
expect(args['attempt']).to eq(1)
expect(args['last_status']).to eq(401)
expect(args['webhook_url_id']).to eq(webhook_url.id)
expect(args['submission_id']).to eq(submission.id)
end
it "doesn't send again if the max attempts is reached" do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('submission_id' => submission.id, 'webhook_url_id' => webhook_url.id,
'attempt' => 11)
end.not_to change(described_class.jobs, :size)
expect(WebMock).to have_requested(:post, webhook_url.url).once
end
end
end
@@ -0,0 +1,104 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe SendTemplateCreatedWebhookRequestJob do
let(:account) { create(:account) }
let(:user) { create(:user, account:) }
let(:template) { create(:template, account:, author: user) }
let(:webhook_url) { create(:webhook_url, account:, events: ['template.created']) }
before do
create(:encrypted_config, key: EncryptedConfig::ESIGN_CERTS_KEY,
value: GenerateCertificate.call.transform_values(&:to_pem))
end
describe '#perform' do
before do
stub_request(:post, webhook_url.url).to_return(status: 200)
end
it 'sends a webhook request' do
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'template.created',
'timestamp' => Time.current,
'data' => Templates::SerializeForApi.call(template.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook'
}
).once
end
it 'sends a webhook request with the secret' do
webhook_url.update(secret: { 'X-Secret-Header' => 'secret_value' })
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'template.created',
'timestamp' => Time.current,
'data' => Templates::SerializeForApi.call(template.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook',
'X-Secret-Header' => 'secret_value'
}
).once
end
it "doesn't send a webhook request if the template doesn't exist" do
expect do
described_class.new.perform('template_id' => 100_500, 'webhook_url_id' => webhook_url.id)
end.to raise_error ActiveRecord::RecordNotFound
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the event is not in the webhook's events" do
webhook_url.update!(events: ['template.updated'])
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the webhook doesn't exist" do
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => 100_500)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it 'sends again if the response status is 400 or higher' do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => webhook_url.id)
end.to change(described_class.jobs, :size).by(1)
expect(WebMock).to have_requested(:post, webhook_url.url).once
args = described_class.jobs.last['args'].first
expect(args['attempt']).to eq(1)
expect(args['last_status']).to eq(401)
expect(args['webhook_url_id']).to eq(webhook_url.id)
expect(args['template_id']).to eq(template.id)
end
it "doesn't send again if the max attempts is reached" do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => webhook_url.id, 'attempt' => 11)
end.not_to change(described_class.jobs, :size)
expect(WebMock).to have_requested(:post, webhook_url.url).once
end
end
end
@@ -0,0 +1,104 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe SendTemplateUpdatedWebhookRequestJob do
let(:account) { create(:account) }
let(:user) { create(:user, account:) }
let(:template) { create(:template, account:, author: user) }
let(:webhook_url) { create(:webhook_url, account:, events: ['template.updated']) }
before do
create(:encrypted_config, key: EncryptedConfig::ESIGN_CERTS_KEY,
value: GenerateCertificate.call.transform_values(&:to_pem))
end
describe '#perform' do
before do
stub_request(:post, webhook_url.url).to_return(status: 200)
end
it 'sends a webhook request' do
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'template.updated',
'timestamp' => Time.current,
'data' => Templates::SerializeForApi.call(template.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook'
}
).once
end
it 'sends a webhook request with the secret' do
webhook_url.update(secret: { 'X-Secret-Header' => 'secret_value' })
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).to have_requested(:post, webhook_url.url).with(
body: replace_timestamps({
'event_type' => 'template.updated',
'timestamp' => Time.current,
'data' => Templates::SerializeForApi.call(template.reload)
}.deep_stringify_keys),
headers: {
'Content-Type' => 'application/json',
'User-Agent' => 'DocuSeal.co Webhook',
'X-Secret-Header' => 'secret_value'
}
).once
end
it "doesn't send a webhook request if the template doesn't exist" do
expect do
described_class.new.perform('template_id' => 100_500, 'webhook_url_id' => webhook_url.id)
end.to raise_error ActiveRecord::RecordNotFound
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the event is not in the webhook's events" do
webhook_url.update!(events: ['template.created'])
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => webhook_url.id)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it "doesn't send a webhook request if the webhook doesn't exist" do
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => 100_500)
expect(WebMock).not_to have_requested(:post, webhook_url.url)
end
it 'sends again if the response status is 400 or higher' do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => webhook_url.id)
end.to change(described_class.jobs, :size).by(1)
expect(WebMock).to have_requested(:post, webhook_url.url).once
args = described_class.jobs.last['args'].first
expect(args['attempt']).to eq(1)
expect(args['last_status']).to eq(401)
expect(args['webhook_url_id']).to eq(webhook_url.id)
expect(args['template_id']).to eq(template.id)
end
it "doesn't send again if the max attempts is reached" do
stub_request(:post, webhook_url.url).to_return(status: 401)
expect do
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => webhook_url.id, 'attempt' => 11)
end.not_to change(described_class.jobs, :size)
expect(WebMock).to have_requested(:post, webhook_url.url).once
end
end
end
+16
View File
@@ -67,3 +67,19 @@ RSpec.configure do |config|
Sidekiq::Testing.inline! if example.metadata[:sidekiq] == :inline
end
end
def replace_timestamps(data, replace = /.*/)
timestamp_fields = %w[created_at updated_at completed_at sent_at opened_at timestamp]
data.each do |key, value|
if timestamp_fields.include?(key) && (value.is_a?(String) || value.is_a?(Time))
data[key] = replace
elsif value.is_a?(Hash)
replace_timestamps(value)
elsif value.is_a?(Array)
value.each { |item| replace_timestamps(item) if item.is_a?(Hash) }
end
end
data
end
+92 -6
View File
@@ -8,24 +8,110 @@ RSpec.describe 'Webhook Settings' do
before do
sign_in(user)
visit settings_webhooks_path
end
it 'shows webhook settings page' do
visit settings_webhooks_path
expect(page).to have_content('Webhooks')
expect(page).to have_field('Webhook URL')
expect(page).to have_button('Save')
WebhookUrl::EVENTS.each do |event|
expect(page).to have_field(event, type: 'checkbox', disabled: true)
end
end
it 'updates the webhook URL' do
fill_in 'Webhook URL', with: 'https://example.com'
it 'creates the webhook' do
visit settings_webhooks_path
fill_in 'Webhook URL', with: 'https://example.com/webhook'
expect do
click_button 'Save'
end.to change(EncryptedConfig, :count).by(1)
end.to change(WebhookUrl, :count).by(1)
encrypted_config = EncryptedConfig.find_by(account:, key: EncryptedConfig::WEBHOOK_URL_KEY)
webhook_url = account.webhook_urls.first
expect(encrypted_config.value).to eq('https://example.com')
expect(webhook_url.url).to eq('https://example.com/webhook')
end
it 'updates the webhook' do
webhook_url = create(:webhook_url, account:, url: 'https://example.com/webhook')
visit settings_webhooks_path
fill_in 'Webhook URL', with: 'https://example.org/webhook'
click_button 'Save'
webhook_url.reload
expect(webhook_url.url).to eq('https://example.org/webhook')
end
it 'deletes the webhook' do
create(:webhook_url, account:)
visit settings_webhooks_path
fill_in 'Webhook URL', with: ''
expect do
click_button 'Save'
end.to change(WebhookUrl, :count).by(-1)
end
it 'updates the webhook events' do
webhook_url = create(:webhook_url, account:)
visit settings_webhooks_path
expect(webhook_url.events).not_to include('submission.created')
check('submission.created')
webhook_url.reload
expect(webhook_url.events).to include('submission.created')
end
it 'adds a secret to the webhook' do
webhook_url = create(:webhook_url, account:)
visit settings_webhooks_path
expect(webhook_url.secret).to be_nil
click_link 'Add Secret'
within '#modal' do
fill_in 'Key', with: 'X-Signature'
fill_in 'Value', with: 'secret-value'
click_button 'Submit'
webhook_url.reload
expect(webhook_url.secret).to eq({ 'X-Signature' => 'secret-value' })
end
end
it 'removes a secret from the webhook' do
webhook_url = create(:webhook_url, account:, secret: { 'X-Signature' => 'secret-value' })
visit settings_webhooks_path
click_link 'Edit Secret'
within '#modal' do
fill_in 'Key', with: ''
fill_in 'Value', with: ''
click_button 'Submit'
webhook_url.reload
expect(webhook_url.secret).to eq({})
end
end
end