Merge from docusealco/wip
This commit is contained in:
@@ -4,6 +4,7 @@ source 'https://rubygems.org'
|
|||||||
|
|
||||||
ruby '4.0.1'
|
ruby '4.0.1'
|
||||||
|
|
||||||
|
gem 'addressable'
|
||||||
gem 'arabic-letter-connector', require: false
|
gem 'arabic-letter-connector', require: false
|
||||||
gem 'aws-sdk-s3', require: false
|
gem 'aws-sdk-s3', require: false
|
||||||
gem 'aws-sdk-secretsmanager', require: false
|
gem 'aws-sdk-secretsmanager', require: false
|
||||||
@@ -28,7 +29,6 @@ gem 'oj'
|
|||||||
gem 'onnxruntime', require: false
|
gem 'onnxruntime', require: false
|
||||||
gem 'pagy'
|
gem 'pagy'
|
||||||
gem 'pg', require: false
|
gem 'pg', require: false
|
||||||
gem 'premailer-rails'
|
|
||||||
gem 'pretender'
|
gem 'pretender'
|
||||||
gem 'puma', require: false
|
gem 'puma', require: false
|
||||||
gem 'rack'
|
gem 'rack'
|
||||||
@@ -43,7 +43,7 @@ gem 'shakapacker'
|
|||||||
gem 'sidekiq'
|
gem 'sidekiq'
|
||||||
gem 'sqlite3', require: false
|
gem 'sqlite3', require: false
|
||||||
gem 'strip_attributes'
|
gem 'strip_attributes'
|
||||||
gem 'trilogy', github: 'trilogy-libraries/trilogy', glob: 'contrib/ruby/*.gemspec', require: false
|
gem 'trilogy', require: false
|
||||||
gem 'turbo-rails'
|
gem 'turbo-rails'
|
||||||
gem 'twitter_cldr', require: false
|
gem 'twitter_cldr', require: false
|
||||||
gem 'tzinfo-data'
|
gem 'tzinfo-data'
|
||||||
|
|||||||
+29
-44
@@ -1,11 +1,3 @@
|
|||||||
GIT
|
|
||||||
remote: https://github.com/trilogy-libraries/trilogy.git
|
|
||||||
revision: 3963d490459df7a2b5bedb42424c3285f25eab22
|
|
||||||
glob: contrib/ruby/*.gemspec
|
|
||||||
specs:
|
|
||||||
trilogy (2.10.0)
|
|
||||||
bigdecimal
|
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
@@ -116,7 +108,7 @@ GEM
|
|||||||
cgi
|
cgi
|
||||||
rexml
|
rexml
|
||||||
base64 (0.3.0)
|
base64 (0.3.0)
|
||||||
bcrypt (3.1.21)
|
bcrypt (3.1.22)
|
||||||
better_html (2.2.0)
|
better_html (2.2.0)
|
||||||
actionview (>= 7.0)
|
actionview (>= 7.0)
|
||||||
activesupport (>= 7.0)
|
activesupport (>= 7.0)
|
||||||
@@ -158,8 +150,6 @@ GEM
|
|||||||
bigdecimal
|
bigdecimal
|
||||||
rexml
|
rexml
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
css_parser (1.21.1)
|
|
||||||
addressable
|
|
||||||
csv (3.3.5)
|
csv (3.3.5)
|
||||||
csv-safe (3.3.1)
|
csv-safe (3.3.1)
|
||||||
csv (~> 3.0)
|
csv (~> 3.0)
|
||||||
@@ -171,16 +161,16 @@ GEM
|
|||||||
irb (~> 1.10)
|
irb (~> 1.10)
|
||||||
reline (>= 0.3.8)
|
reline (>= 0.3.8)
|
||||||
declarative (0.0.20)
|
declarative (0.0.20)
|
||||||
devise (4.9.4)
|
devise (5.0.3)
|
||||||
bcrypt (~> 3.0)
|
bcrypt (~> 3.0)
|
||||||
orm_adapter (~> 0.1)
|
orm_adapter (~> 0.1)
|
||||||
railties (>= 4.1.0)
|
railties (>= 7.0)
|
||||||
responders
|
responders
|
||||||
warden (~> 1.2.3)
|
warden (~> 1.2.3)
|
||||||
devise-two-factor (6.3.1)
|
devise-two-factor (6.4.0)
|
||||||
activesupport (>= 7.0, < 8.2)
|
activesupport (>= 7.2, < 8.2)
|
||||||
devise (>= 4.0, < 5.0)
|
devise (>= 4.0, < 6.0)
|
||||||
railties (>= 7.0, < 8.2)
|
railties (>= 7.2, < 8.2)
|
||||||
rotp (~> 6.0)
|
rotp (~> 6.0)
|
||||||
diff-lcs (1.6.2)
|
diff-lcs (1.6.2)
|
||||||
digest-crc (0.7.0)
|
digest-crc (0.7.0)
|
||||||
@@ -189,7 +179,7 @@ GEM
|
|||||||
dotenv (3.2.0)
|
dotenv (3.2.0)
|
||||||
drb (2.2.3)
|
drb (2.2.3)
|
||||||
email_typo (0.2.3)
|
email_typo (0.2.3)
|
||||||
erb (6.0.1)
|
erb (6.0.2)
|
||||||
erb_lint (0.9.0)
|
erb_lint (0.9.0)
|
||||||
activesupport
|
activesupport
|
||||||
better_html (>= 2.0.1)
|
better_html (>= 2.0.1)
|
||||||
@@ -272,19 +262,19 @@ GEM
|
|||||||
geom2d (~> 0.4, >= 0.4.1)
|
geom2d (~> 0.4, >= 0.4.1)
|
||||||
openssl (>= 2.2.1)
|
openssl (>= 2.2.1)
|
||||||
strscan (>= 3.1.2)
|
strscan (>= 3.1.2)
|
||||||
htmlentities (4.4.2)
|
|
||||||
i18n (1.14.8)
|
i18n (1.14.8)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
image_processing (1.14.0)
|
image_processing (1.14.0)
|
||||||
mini_magick (>= 4.9.5, < 6)
|
mini_magick (>= 4.9.5, < 6)
|
||||||
ruby-vips (>= 2.0.17, < 3)
|
ruby-vips (>= 2.0.17, < 3)
|
||||||
io-console (0.8.2)
|
io-console (0.8.2)
|
||||||
irb (1.16.0)
|
irb (1.17.0)
|
||||||
pp (>= 0.6.0)
|
pp (>= 0.6.0)
|
||||||
|
prism (>= 1.3.0)
|
||||||
rdoc (>= 4.0.0)
|
rdoc (>= 4.0.0)
|
||||||
reline (>= 0.4.2)
|
reline (>= 0.4.2)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.18.1)
|
json (2.19.2)
|
||||||
jwt (3.1.2)
|
jwt (3.1.2)
|
||||||
base64
|
base64
|
||||||
language_server-protocol (3.17.0.5)
|
language_server-protocol (3.17.0.5)
|
||||||
@@ -306,7 +296,7 @@ GEM
|
|||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
railties (>= 4)
|
railties (>= 4)
|
||||||
request_store (~> 1.0)
|
request_store (~> 1.0)
|
||||||
loofah (2.25.0)
|
loofah (2.25.1)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.12.0)
|
nokogiri (>= 1.12.0)
|
||||||
mail (2.9.0)
|
mail (2.9.0)
|
||||||
@@ -321,7 +311,8 @@ GEM
|
|||||||
mini_magick (5.3.1)
|
mini_magick (5.3.1)
|
||||||
logger
|
logger
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
minitest (6.0.1)
|
minitest (6.0.2)
|
||||||
|
drb (~> 2.0)
|
||||||
prism (~> 1.5)
|
prism (~> 1.5)
|
||||||
msgpack (1.8.0)
|
msgpack (1.8.0)
|
||||||
multi_json (1.19.1)
|
multi_json (1.19.1)
|
||||||
@@ -337,17 +328,17 @@ GEM
|
|||||||
net-smtp (0.5.1)
|
net-smtp (0.5.1)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.7.5)
|
nio4r (2.7.5)
|
||||||
nokogiri (1.19.1-aarch64-linux-gnu)
|
nokogiri (1.19.2-aarch64-linux-gnu)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.19.1-aarch64-linux-musl)
|
nokogiri (1.19.2-aarch64-linux-musl)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.19.1-arm64-darwin)
|
nokogiri (1.19.2-arm64-darwin)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.19.1-x86_64-linux-gnu)
|
nokogiri (1.19.2-x86_64-linux-gnu)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
nokogiri (1.19.1-x86_64-linux-musl)
|
nokogiri (1.19.2-x86_64-linux-musl)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
numo-narray-alt (0.9.13)
|
numo-narray-alt (0.10.3)
|
||||||
oj (3.16.13)
|
oj (3.16.13)
|
||||||
bigdecimal (>= 3.0)
|
bigdecimal (>= 3.0)
|
||||||
ostruct (>= 0.2)
|
ostruct (>= 0.2)
|
||||||
@@ -376,18 +367,10 @@ GEM
|
|||||||
pg (1.6.3-x86_64-linux-musl)
|
pg (1.6.3-x86_64-linux-musl)
|
||||||
pp (0.6.3)
|
pp (0.6.3)
|
||||||
prettyprint
|
prettyprint
|
||||||
premailer (1.27.0)
|
|
||||||
addressable
|
|
||||||
css_parser (>= 1.19.0)
|
|
||||||
htmlentities (>= 4.0.0)
|
|
||||||
premailer-rails (1.12.0)
|
|
||||||
actionmailer (>= 3)
|
|
||||||
net-smtp
|
|
||||||
premailer (~> 1.7, >= 1.7.9)
|
|
||||||
pretender (0.6.0)
|
pretender (0.6.0)
|
||||||
actionpack (>= 7.1)
|
actionpack (>= 7.1)
|
||||||
prettyprint (0.2.0)
|
prettyprint (0.2.0)
|
||||||
prism (1.8.0)
|
prism (1.9.0)
|
||||||
pry (0.16.0)
|
pry (0.16.0)
|
||||||
coderay (~> 1.1)
|
coderay (~> 1.1)
|
||||||
method_source (~> 1.0)
|
method_source (~> 1.0)
|
||||||
@@ -429,8 +412,8 @@ GEM
|
|||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
minitest
|
minitest
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.6.2)
|
rails-html-sanitizer (1.7.0)
|
||||||
loofah (~> 2.21)
|
loofah (~> 2.25)
|
||||||
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
||||||
rails-i18n (8.1.0)
|
rails-i18n (8.1.0)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
@@ -446,7 +429,7 @@ GEM
|
|||||||
zeitwerk (~> 2.6)
|
zeitwerk (~> 2.6)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
rake (13.3.1)
|
rake (13.3.1)
|
||||||
rdoc (7.1.0)
|
rdoc (7.2.0)
|
||||||
erb
|
erb
|
||||||
psych (>= 4.0.0)
|
psych (>= 4.0.0)
|
||||||
tsort
|
tsort
|
||||||
@@ -562,6 +545,8 @@ GEM
|
|||||||
thor (1.5.0)
|
thor (1.5.0)
|
||||||
timeout (0.6.0)
|
timeout (0.6.0)
|
||||||
trailblazer-option (0.1.2)
|
trailblazer-option (0.1.2)
|
||||||
|
trilogy (2.10.0)
|
||||||
|
bigdecimal
|
||||||
tsort (0.2.0)
|
tsort (0.2.0)
|
||||||
turbo-rails (2.0.21)
|
turbo-rails (2.0.21)
|
||||||
actionpack (>= 7.1.0)
|
actionpack (>= 7.1.0)
|
||||||
@@ -601,7 +586,7 @@ GEM
|
|||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
yaml (0.4.0)
|
yaml (0.4.0)
|
||||||
zeitwerk (2.7.4)
|
zeitwerk (2.7.5)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
aarch64-linux
|
aarch64-linux
|
||||||
@@ -611,6 +596,7 @@ PLATFORMS
|
|||||||
x86_64-linux-musl
|
x86_64-linux-musl
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
addressable
|
||||||
annotaterb
|
annotaterb
|
||||||
arabic-letter-connector
|
arabic-letter-connector
|
||||||
aws-sdk-s3
|
aws-sdk-s3
|
||||||
@@ -647,7 +633,6 @@ DEPENDENCIES
|
|||||||
onnxruntime
|
onnxruntime
|
||||||
pagy
|
pagy
|
||||||
pg
|
pg
|
||||||
premailer-rails
|
|
||||||
pretender
|
pretender
|
||||||
pry-rails
|
pry-rails
|
||||||
puma
|
puma
|
||||||
@@ -669,7 +654,7 @@ DEPENDENCIES
|
|||||||
simplecov
|
simplecov
|
||||||
sqlite3
|
sqlite3
|
||||||
strip_attributes
|
strip_attributes
|
||||||
trilogy!
|
trilogy
|
||||||
turbo-rails
|
turbo-rails
|
||||||
twitter_cldr
|
twitter_cldr
|
||||||
tzinfo-data
|
tzinfo-data
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ module Api
|
|||||||
SearchEntries.enqueue_reindex(@template)
|
SearchEntries.enqueue_reindex(@template)
|
||||||
|
|
||||||
WebhookUrls.enqueue_events(@template, 'template.updated')
|
WebhookUrls.enqueue_events(@template, 'template.updated')
|
||||||
|
WebhookUrls.enqueue_events(@template, 'template.archived') if archived == true
|
||||||
|
|
||||||
render json: @template.as_json(only: %i[id updated_at])
|
render json: @template.as_json(only: %i[id updated_at])
|
||||||
end
|
end
|
||||||
@@ -78,6 +79,8 @@ module Api
|
|||||||
@template.destroy!
|
@template.destroy!
|
||||||
else
|
else
|
||||||
@template.update!(archived_at: Time.current)
|
@template.update!(archived_at: Time.current)
|
||||||
|
|
||||||
|
WebhookUrls.enqueue_events(@template, 'template.archived')
|
||||||
end
|
end
|
||||||
|
|
||||||
render json: @template.as_json(only: %i[id archived_at])
|
render json: @template.as_json(only: %i[id archived_at])
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ class TemplatesController < ApplicationController
|
|||||||
else
|
else
|
||||||
@template.update!(archived_at: Time.current)
|
@template.update!(archived_at: Time.current)
|
||||||
|
|
||||||
|
WebhookUrls.enqueue_events(@template, 'template.archived')
|
||||||
|
|
||||||
I18n.t('template_has_been_archived')
|
I18n.t('template_has_been_archived')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ class TemplatesRestoreController < ApplicationController
|
|||||||
def create
|
def create
|
||||||
@template.update!(archived_at: nil)
|
@template.update!(archived_at: nil)
|
||||||
|
|
||||||
|
WebhookUrls.enqueue_events(@template, 'template.updated')
|
||||||
|
|
||||||
redirect_to template_path(@template), notice: I18n.t('template_has_been_unarchived')
|
redirect_to template_path(@template), notice: I18n.t('template_has_been_unarchived')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class WebhookEventsController < ApplicationController
|
|||||||
Submissions::SerializeForApi.call(@webhook_event.record)
|
Submissions::SerializeForApi.call(@webhook_event.record)
|
||||||
when 'template.created', 'template.updated'
|
when 'template.created', 'template.updated'
|
||||||
Templates::SerializeForApi.call(@webhook_event.record)
|
Templates::SerializeForApi.call(@webhook_event.record)
|
||||||
when 'submission.archived'
|
when 'submission.archived', 'template.archived'
|
||||||
@webhook_event.record.as_json(only: %i[id archived_at])
|
@webhook_event.record.as_json(only: %i[id archived_at])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2735,10 +2735,13 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.isSaving = true
|
this.isSaving = true
|
||||||
|
|
||||||
this.documentRefs.filter((ref) => ref.update).map((ref) => ref.update())
|
const dynamicDocumentRefs = this.documentRefs.filter((ref) => ref.isDynamic)
|
||||||
|
|
||||||
|
dynamicDocumentRefs.map((ref) => ref.update())
|
||||||
|
|
||||||
this.rebuildVariablesSchema({ disable: false })
|
this.rebuildVariablesSchema({ disable: false })
|
||||||
|
|
||||||
const dynamicDocumentSaves = this.documentRefs.filter((ref) => ref.saveBody).map((ref) => ref.saveBody())
|
const dynamicDocumentSaves = dynamicDocumentRefs.map((ref) => ref.saveBody())
|
||||||
|
|
||||||
Promise.all([this.save(), ...dynamicDocumentSaves]).then(() => {
|
Promise.all([this.save(), ...dynamicDocumentSaves]).then(() => {
|
||||||
window.Turbo.visit(`/templates/${this.template.id}`)
|
window.Turbo.visit(`/templates/${this.template.id}`)
|
||||||
@@ -3028,6 +3031,8 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
dynamicDocumentRef.syncVariablesSchema(this.template.variables_schema, parsed, { disable })
|
dynamicDocumentRef.syncVariablesSchema(this.template.variables_schema, parsed, { disable })
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.template.variables_schema = {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class SendTemplateArchivedWebhookRequestJob
|
||||||
|
include Sidekiq::Job
|
||||||
|
|
||||||
|
sidekiq_options queue: :webhooks
|
||||||
|
|
||||||
|
MAX_ATTEMPTS = 10
|
||||||
|
|
||||||
|
def perform(params = {})
|
||||||
|
template = Template.find_by(id: params['template_id'])
|
||||||
|
|
||||||
|
return unless template
|
||||||
|
|
||||||
|
webhook_url = WebhookUrl.find_by(id: params['webhook_url_id'])
|
||||||
|
|
||||||
|
return unless webhook_url
|
||||||
|
|
||||||
|
attempt = params['attempt'].to_i
|
||||||
|
|
||||||
|
return if webhook_url.url.blank? || webhook_url.events.exclude?('template.archived')
|
||||||
|
|
||||||
|
resp = SendWebhookRequest.call(webhook_url, event_type: 'template.archived',
|
||||||
|
event_uuid: params['event_uuid'],
|
||||||
|
record: template,
|
||||||
|
attempt:,
|
||||||
|
data: template.as_json(only: %i[id archived_at]))
|
||||||
|
|
||||||
|
if (resp.nil? || resp.status.to_i >= 400) && attempt <= MAX_ATTEMPTS &&
|
||||||
|
(!Docuseal.multitenant? || template.account.account_configs.exists?(key: :plan))
|
||||||
|
SendTemplateArchivedWebhookRequestJob.perform_in((2**attempt).minutes, {
|
||||||
|
**params,
|
||||||
|
'attempt' => attempt + 1,
|
||||||
|
'last_status' => resp&.status.to_i
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -5,6 +5,8 @@ class ApplicationMailer < ActionMailer::Base
|
|||||||
layout 'mailer'
|
layout 'mailer'
|
||||||
|
|
||||||
register_interceptor ActionMailerConfigsInterceptor
|
register_interceptor ActionMailerConfigsInterceptor
|
||||||
|
register_interceptor HtmlToPlainTextInterceptor
|
||||||
|
register_preview_interceptor HtmlToPlainTextInterceptor
|
||||||
|
|
||||||
register_observer ActionMailerEventsObserver
|
register_observer ActionMailerEventsObserver
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class WebhookUrl < ApplicationRecord
|
|||||||
submission.archived
|
submission.archived
|
||||||
template.created
|
template.created
|
||||||
template.updated
|
template.updated
|
||||||
|
template.archived
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
|||||||
@@ -88,6 +88,8 @@ Rails.application.configure do
|
|||||||
openssl_verify_mode: ENV['SMTP_SSL_VERIFY'] == 'false' ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER,
|
openssl_verify_mode: ENV['SMTP_SSL_VERIFY'] == 'false' ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER,
|
||||||
authentication: ENV.fetch('SMTP_PASSWORD', nil).present? ? ENV.fetch('SMTP_AUTHENTICATION', 'plain') : nil,
|
authentication: ENV.fetch('SMTP_PASSWORD', nil).present? ? ENV.fetch('SMTP_AUTHENTICATION', 'plain') : nil,
|
||||||
enable_starttls: ENV['SMTP_ENABLE_STARTTLS'] != 'false',
|
enable_starttls: ENV['SMTP_ENABLE_STARTTLS'] != 'false',
|
||||||
|
ssl: ENV['SMTP_ENABLE_SSL'] == 'true',
|
||||||
|
tls: ENV['SMTP_ENABLE_TLS'] == 'true',
|
||||||
open_timeout: ENV.fetch('SMTP_OPEN_TIMEOUT', '15').to_i,
|
open_timeout: ENV.fetch('SMTP_OPEN_TIMEOUT', '15').to_i,
|
||||||
read_timeout: ENV.fetch('SMTP_READ_TIMEOUT', '25').to_i
|
read_timeout: ENV.fetch('SMTP_READ_TIMEOUT', '25').to_i
|
||||||
}.compact
|
}.compact
|
||||||
|
|||||||
@@ -914,6 +914,9 @@ en: &en
|
|||||||
connect_to_docuseal_mcp: Connect to DocuSeal MCP
|
connect_to_docuseal_mcp: Connect to DocuSeal MCP
|
||||||
add_the_following_to_your_mcp_client_configuration: Add the following to your MCP client configuration
|
add_the_following_to_your_mcp_client_configuration: Add the following to your MCP client configuration
|
||||||
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Works with Claude Desktop, Cursor, Windsurf, VS Code, and any MCP-compatible client.
|
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Works with Claude Desktop, Cursor, Windsurf, VS Code, and any MCP-compatible client.
|
||||||
|
your_email_address_has_been_changed: Your email address has been changed
|
||||||
|
the_email_address_for_your_account_has_been_changed_to_new_email: The email address for your account has been changed to %{new_email}.
|
||||||
|
if_you_did_not_make_this_change_please_contact_us_by_replying_to_this_email: If you did not make this change, please contact us by replying to this email.
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Your email address has been successfully confirmed.
|
confirmed: Your email address has been successfully confirmed.
|
||||||
@@ -1938,6 +1941,9 @@ es: &es
|
|||||||
connect_to_docuseal_mcp: Conectar a DocuSeal MCP
|
connect_to_docuseal_mcp: Conectar a DocuSeal MCP
|
||||||
add_the_following_to_your_mcp_client_configuration: Agregue lo siguiente a la configuración de su cliente MCP
|
add_the_following_to_your_mcp_client_configuration: Agregue lo siguiente a la configuración de su cliente MCP
|
||||||
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Funciona con Claude Desktop, Cursor, Windsurf, VS Code y cualquier cliente compatible con MCP.
|
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Funciona con Claude Desktop, Cursor, Windsurf, VS Code y cualquier cliente compatible con MCP.
|
||||||
|
your_email_address_has_been_changed: Tu dirección de correo electrónico ha sido cambiada
|
||||||
|
the_email_address_for_your_account_has_been_changed_to_new_email: La dirección de correo electrónico de tu cuenta ha sido cambiada a %{new_email}.
|
||||||
|
if_you_did_not_make_this_change_please_contact_us_by_replying_to_this_email: Si no realizaste este cambio, contáctanos respondiendo a este correo electrónico.
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Tu dirección de correo electrónico ha sido confirmada correctamente.
|
confirmed: Tu dirección de correo electrónico ha sido confirmada correctamente.
|
||||||
@@ -2963,6 +2969,9 @@ it: &it
|
|||||||
connect_to_docuseal_mcp: Connetti a DocuSeal MCP
|
connect_to_docuseal_mcp: Connetti a DocuSeal MCP
|
||||||
add_the_following_to_your_mcp_client_configuration: Aggiungi quanto segue alla configurazione del tuo client MCP
|
add_the_following_to_your_mcp_client_configuration: Aggiungi quanto segue alla configurazione del tuo client MCP
|
||||||
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Funziona con Claude Desktop, Cursor, Windsurf, VS Code e qualsiasi client compatibile con MCP.
|
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Funziona con Claude Desktop, Cursor, Windsurf, VS Code e qualsiasi client compatibile con MCP.
|
||||||
|
your_email_address_has_been_changed: Il tuo indirizzo email è stato modificato
|
||||||
|
the_email_address_for_your_account_has_been_changed_to_new_email: L'indirizzo email del tuo account è stato modificato in %{new_email}.
|
||||||
|
if_you_did_not_make_this_change_please_contact_us_by_replying_to_this_email: Se non hai effettuato questa modifica, contattaci rispondendo a questa email.
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Il tuo indirizzo email è stato confermato con successo.
|
confirmed: Il tuo indirizzo email è stato confermato con successo.
|
||||||
@@ -3984,6 +3993,9 @@ fr: &fr
|
|||||||
connect_to_docuseal_mcp: Se connecter à DocuSeal MCP
|
connect_to_docuseal_mcp: Se connecter à DocuSeal MCP
|
||||||
add_the_following_to_your_mcp_client_configuration: Ajoutez ce qui suit à la configuration de votre client MCP
|
add_the_following_to_your_mcp_client_configuration: Ajoutez ce qui suit à la configuration de votre client MCP
|
||||||
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Fonctionne avec Claude Desktop, Cursor, Windsurf, VS Code et tout client compatible MCP.
|
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Fonctionne avec Claude Desktop, Cursor, Windsurf, VS Code et tout client compatible MCP.
|
||||||
|
your_email_address_has_been_changed: Votre adresse e-mail a été modifiée
|
||||||
|
the_email_address_for_your_account_has_been_changed_to_new_email: "L'adresse e-mail de votre compte a été modifiée en %{new_email}."
|
||||||
|
if_you_did_not_make_this_change_please_contact_us_by_replying_to_this_email: Si vous n'avez pas effectué ce changement, veuillez nous contacter en répondant à cet e-mail.
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Votre adresse e-mail a été confirmée avec succès.
|
confirmed: Votre adresse e-mail a été confirmée avec succès.
|
||||||
@@ -5008,6 +5020,9 @@ pt: &pt
|
|||||||
connect_to_docuseal_mcp: Conectar ao DocuSeal MCP
|
connect_to_docuseal_mcp: Conectar ao DocuSeal MCP
|
||||||
add_the_following_to_your_mcp_client_configuration: Adicione o seguinte à configuração do seu cliente MCP
|
add_the_following_to_your_mcp_client_configuration: Adicione o seguinte à configuração do seu cliente MCP
|
||||||
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Funciona com Claude Desktop, Cursor, Windsurf, VS Code e qualquer cliente compatível com MCP.
|
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Funciona com Claude Desktop, Cursor, Windsurf, VS Code e qualquer cliente compatível com MCP.
|
||||||
|
your_email_address_has_been_changed: Seu endereço de e-mail foi alterado
|
||||||
|
the_email_address_for_your_account_has_been_changed_to_new_email: O endereço de e-mail da sua conta foi alterado para %{new_email}.
|
||||||
|
if_you_did_not_make_this_change_please_contact_us_by_replying_to_this_email: Se você não fez essa alteração, entre em contato conosco respondendo a este e-mail.
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Seu endereço de e-mail foi confirmado com sucesso.
|
confirmed: Seu endereço de e-mail foi confirmado com sucesso.
|
||||||
@@ -6032,6 +6047,9 @@ de: &de
|
|||||||
connect_to_docuseal_mcp: Mit DocuSeal MCP verbinden
|
connect_to_docuseal_mcp: Mit DocuSeal MCP verbinden
|
||||||
add_the_following_to_your_mcp_client_configuration: Fügen Sie Folgendes zu Ihrer MCP-Client-Konfiguration hinzu
|
add_the_following_to_your_mcp_client_configuration: Fügen Sie Folgendes zu Ihrer MCP-Client-Konfiguration hinzu
|
||||||
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Funktioniert mit Claude Desktop, Cursor, Windsurf, VS Code und jedem MCP-kompatiblen Client.
|
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Funktioniert mit Claude Desktop, Cursor, Windsurf, VS Code und jedem MCP-kompatiblen Client.
|
||||||
|
your_email_address_has_been_changed: Ihre E-Mail-Adresse wurde geändert
|
||||||
|
the_email_address_for_your_account_has_been_changed_to_new_email: Die E-Mail-Adresse Ihres Kontos wurde in %{new_email} geändert.
|
||||||
|
if_you_did_not_make_this_change_please_contact_us_by_replying_to_this_email: Wenn Sie diese Änderung nicht vorgenommen haben, kontaktieren Sie uns bitte, indem Sie auf diese E-Mail antworten.
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Ihre E-Mail-Adresse wurde erfolgreich bestätigt.
|
confirmed: Ihre E-Mail-Adresse wurde erfolgreich bestätigt.
|
||||||
@@ -7441,6 +7459,9 @@ nl: &nl
|
|||||||
connect_to_docuseal_mcp: Verbinden met DocuSeal MCP
|
connect_to_docuseal_mcp: Verbinden met DocuSeal MCP
|
||||||
add_the_following_to_your_mcp_client_configuration: Voeg het volgende toe aan uw MCP-clientconfiguratie
|
add_the_following_to_your_mcp_client_configuration: Voeg het volgende toe aan uw MCP-clientconfiguratie
|
||||||
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Werkt met Claude Desktop, Cursor, Windsurf, VS Code en elke MCP-compatibele client.
|
works_with_claude_desktop_cursor_windsurf_vs_code_and_any_mcp_compatible_client: Werkt met Claude Desktop, Cursor, Windsurf, VS Code en elke MCP-compatibele client.
|
||||||
|
your_email_address_has_been_changed: Je e-mailadres is gewijzigd
|
||||||
|
the_email_address_for_your_account_has_been_changed_to_new_email: Het e-mailadres van je account is gewijzigd naar %{new_email}.
|
||||||
|
if_you_did_not_make_this_change_please_contact_us_by_replying_to_this_email: Als je deze wijziging niet hebt aangebracht, neem dan contact met ons op door op deze e-mail te antwoorden.
|
||||||
devise:
|
devise:
|
||||||
confirmations:
|
confirmations:
|
||||||
confirmed: Je e-mailadres is succesvol bevestigd.
|
confirmed: Je e-mailadres is succesvol bevestigd.
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module HtmlToPlainText
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def call(html, line_length = 65)
|
||||||
|
return '' if html.nil? || html.strip.empty?
|
||||||
|
|
||||||
|
cleaned = html.gsub(%r{<!-- start text/html -->.*?<!-- end text/html -->}m, '')
|
||||||
|
|
||||||
|
doc = Nokogiri::HTML.fragment(cleaned)
|
||||||
|
|
||||||
|
doc.css('script').each(&:remove)
|
||||||
|
|
||||||
|
result = process_nodes(doc, line_length)
|
||||||
|
|
||||||
|
result.gsub!(/\r\n?/, "\n")
|
||||||
|
result.gsub!(/[ \t]*\u00A0+[ \t]*/, ' ')
|
||||||
|
result.gsub!(/\n[ \t]+/, "\n")
|
||||||
|
result.gsub!(/[ \t]+\n/, "\n")
|
||||||
|
result.gsub!(/\n{3,}/, "\n\n")
|
||||||
|
|
||||||
|
result = word_wrap(result, line_length)
|
||||||
|
|
||||||
|
result.gsub!(/\(([ \n])(http[^)]+)([\n ])\)/) do
|
||||||
|
"#{"\n" if ::Regexp.last_match(1) == "\n"}( #{::Regexp.last_match(2)} )#{"\n" if ::Regexp.last_match(3) == "\n"}"
|
||||||
|
end
|
||||||
|
|
||||||
|
result.strip
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_nodes(node, line_length)
|
||||||
|
result = +''
|
||||||
|
|
||||||
|
node.children.each do |child|
|
||||||
|
case child
|
||||||
|
when Nokogiri::XML::Text
|
||||||
|
result << child.text
|
||||||
|
when Nokogiri::XML::Comment
|
||||||
|
next
|
||||||
|
when Nokogiri::XML::Element
|
||||||
|
result << process_element(child, line_length)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_element(node, line_length)
|
||||||
|
case node.name
|
||||||
|
when 'br'
|
||||||
|
"\n"
|
||||||
|
when 'p', 'div'
|
||||||
|
inner = process_nodes(node, line_length)
|
||||||
|
inner.strip.empty? ? '' : "#{inner}\n\n"
|
||||||
|
when 'img'
|
||||||
|
node['alt'] || ''
|
||||||
|
when 'a'
|
||||||
|
process_link(node, line_length)
|
||||||
|
when /\Ah([1-6])\z/
|
||||||
|
process_heading(node, ::Regexp.last_match(1).to_i, line_length)
|
||||||
|
when 'li'
|
||||||
|
inner = process_nodes(node, line_length)
|
||||||
|
"* #{inner.strip}\n"
|
||||||
|
else
|
||||||
|
process_nodes(node, line_length)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_link(node, line_length)
|
||||||
|
text = process_nodes(node, line_length).strip
|
||||||
|
return '' if text.empty?
|
||||||
|
|
||||||
|
href = node['href']
|
||||||
|
href = href.sub(/\Amailto:/i, '') if href
|
||||||
|
|
||||||
|
if href.nil? || text.casecmp(href.strip) == 0
|
||||||
|
text
|
||||||
|
else
|
||||||
|
"#{text} ( #{href.strip} )"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_heading(node, level, line_length)
|
||||||
|
text = +''
|
||||||
|
node.children.each do |child|
|
||||||
|
text << if child.name == 'br'
|
||||||
|
"\n"
|
||||||
|
else
|
||||||
|
child.text
|
||||||
|
end
|
||||||
|
end
|
||||||
|
text.strip!
|
||||||
|
|
||||||
|
hlength = text.each_line.map { |l| l.strip.length }.max || 0
|
||||||
|
hlength = line_length if hlength > line_length
|
||||||
|
|
||||||
|
decorated = case level
|
||||||
|
when 1
|
||||||
|
"#{'*' * hlength}\n#{text}\n#{'*' * hlength}"
|
||||||
|
when 2
|
||||||
|
"#{'-' * hlength}\n#{text}\n#{'-' * hlength}"
|
||||||
|
else
|
||||||
|
"#{text}\n#{'-' * hlength}"
|
||||||
|
end
|
||||||
|
|
||||||
|
"\n\n#{decorated}\n\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
def word_wrap(txt, line_length)
|
||||||
|
txt.split("\n").map do |line|
|
||||||
|
if line.length > line_length
|
||||||
|
line.gsub(/(.{1,#{line_length}})(\s+|$)/, "\\1\n").strip
|
||||||
|
else
|
||||||
|
line
|
||||||
|
end
|
||||||
|
end.join("\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module HtmlToPlainTextInterceptor
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def delivering_email(message)
|
||||||
|
process(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def previewing_email(message)
|
||||||
|
process(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def process(message)
|
||||||
|
return message unless html_part(message)
|
||||||
|
return message if message.text_part
|
||||||
|
|
||||||
|
add_text_part(message)
|
||||||
|
|
||||||
|
message
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_text_part(message)
|
||||||
|
html = html_part(message).decoded
|
||||||
|
text = HtmlToPlainText.call(html)
|
||||||
|
|
||||||
|
text_part = Mail::Part.new do
|
||||||
|
content_type 'text/plain; charset=UTF-8'
|
||||||
|
body text
|
||||||
|
end
|
||||||
|
|
||||||
|
if pure_html_message?(message)
|
||||||
|
message.body = nil
|
||||||
|
message.content_type = 'multipart/alternative'
|
||||||
|
message.add_part(text_part)
|
||||||
|
message.add_part(Mail::Part.new do
|
||||||
|
content_type 'text/html; charset=UTF-8'
|
||||||
|
body html
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
alternative = Mail::Part.new(content_type: 'multipart/alternative')
|
||||||
|
alternative.add_part(text_part)
|
||||||
|
alternative.add_part(message.html_part)
|
||||||
|
replace_part(message.parts, message.html_part, alternative)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def pure_html_message?(message)
|
||||||
|
message.content_type.to_s.include?('text/html')
|
||||||
|
end
|
||||||
|
|
||||||
|
def html_part(message)
|
||||||
|
pure_html_message?(message) ? message : message.html_part
|
||||||
|
end
|
||||||
|
|
||||||
|
def replace_part(parts, old_part, new_part)
|
||||||
|
if (index = parts.index(old_part))
|
||||||
|
parts[index] = new_part
|
||||||
|
else
|
||||||
|
parts.each do |part|
|
||||||
|
replace_part(part.parts, old_part, new_part) if part.respond_to?(:parts)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -236,9 +236,10 @@ module Submissions
|
|||||||
|
|
||||||
page[:Annots] ||= []
|
page[:Annots] ||= []
|
||||||
page[:Annots] = page[:Annots].try(:reject) do |e|
|
page[:Annots] = page[:Annots].try(:reject) do |e|
|
||||||
next if e.is_a?(Integer) || e.is_a?(Symbol)
|
next if e.is_a?(Integer) || e.is_a?(Symbol) || e.is_a?(HexaPDF::PDFArray)
|
||||||
|
|
||||||
e.present? && e[:A] && e[:A][:URI].to_s.starts_with?('file:///docuseal_field')
|
e.present? && e[:A] && !e[:A].is_a?(HexaPDF::PDFArray) &&
|
||||||
|
e[:A][:URI].to_s.starts_with?('file:///docuseal_field')
|
||||||
end || page[:Annots]
|
end || page[:Annots]
|
||||||
|
|
||||||
width = page.box.width
|
width = page.box.width
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ module Templates
|
|||||||
pdf.pages.flat_map.with_index do |page, index|
|
pdf.pages.flat_map.with_index do |page, index|
|
||||||
(page[:Annots] || []).filter_map do |annot|
|
(page[:Annots] || []).filter_map do |annot|
|
||||||
next if annot.blank?
|
next if annot.blank?
|
||||||
next if annot.is_a?(Integer) || annot.is_a?(Symbol)
|
next if annot.is_a?(Integer) || annot.is_a?(Symbol) || annot.is_a?(HexaPDF::PDFArray)
|
||||||
next if annot[:A].blank? || annot[:A][:URI].blank?
|
next if annot[:A].blank? || annot[:A][:URI].blank?
|
||||||
next unless annot[:Subtype] == :Link
|
next unless annot[:Subtype] == :Link
|
||||||
next if !annot[:A][:URI].starts_with?('https://') && !annot[:A][:URI].starts_with?('http://')
|
next if !annot[:A][:URI].starts_with?('https://') && !annot[:A][:URI].starts_with?('http://')
|
||||||
|
|||||||
+2
-1
@@ -11,7 +11,8 @@ module WebhookUrls
|
|||||||
'submission.expired' => SendSubmissionExpiredWebhookRequestJob,
|
'submission.expired' => SendSubmissionExpiredWebhookRequestJob,
|
||||||
'submission.archived' => SendSubmissionArchivedWebhookRequestJob,
|
'submission.archived' => SendSubmissionArchivedWebhookRequestJob,
|
||||||
'template.created' => SendTemplateCreatedWebhookRequestJob,
|
'template.created' => SendTemplateCreatedWebhookRequestJob,
|
||||||
'template.updated' => SendTemplateUpdatedWebhookRequestJob
|
'template.updated' => SendTemplateUpdatedWebhookRequestJob,
|
||||||
|
'template.archived' => SendTemplateArchivedWebhookRequestJob
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
EVENT_TYPE_ID_KEYS = {
|
EVENT_TYPE_ID_KEYS = {
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.describe SendTemplateArchivedWebhookRequestJob 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.archived']) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
create(:encrypted_config, key: EncryptedConfig::ESIGN_CERTS_KEY,
|
||||||
|
value: GenerateCertificate.call.transform_values(&:to_pem))
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#perform' do
|
||||||
|
around do |example|
|
||||||
|
freeze_time { example.run }
|
||||||
|
end
|
||||||
|
|
||||||
|
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,
|
||||||
|
'event_uuid' => SecureRandom.uuid)
|
||||||
|
|
||||||
|
expect(WebMock).to have_requested(:post, webhook_url.url).with(
|
||||||
|
body: {
|
||||||
|
'event_type' => 'template.archived',
|
||||||
|
'timestamp' => /.*/,
|
||||||
|
'data' => template.reload.as_json(only: %i[id archived_at])
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'User-Agent' => 'DocuSeal.com 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,
|
||||||
|
'event_uuid' => SecureRandom.uuid)
|
||||||
|
|
||||||
|
expect(WebMock).to have_requested(:post, webhook_url.url).with(
|
||||||
|
body: {
|
||||||
|
'event_type' => 'template.archived',
|
||||||
|
'timestamp' => /.*/,
|
||||||
|
'data' => template.reload.as_json(only: %i[id archived_at])
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'User-Agent' => 'DocuSeal.com Webhook',
|
||||||
|
'X-Secret-Header' => 'secret_value'
|
||||||
|
}
|
||||||
|
).once
|
||||||
|
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,
|
||||||
|
'event_uuid' => SecureRandom.uuid)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
event_uuid = SecureRandom.uuid
|
||||||
|
|
||||||
|
expect do
|
||||||
|
described_class.new.perform('template_id' => template.id, 'webhook_url_id' => webhook_url.id,
|
||||||
|
'event_uuid' => event_uuid)
|
||||||
|
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['event_uuid']).to eq(event_uuid)
|
||||||
|
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,
|
||||||
|
'event_uuid' => SecureRandom.uuid, 'attempt' => 11)
|
||||||
|
end.not_to change(described_class.jobs, :size)
|
||||||
|
|
||||||
|
expect(WebMock).to have_requested(:post, webhook_url.url).once
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -23,7 +23,7 @@ RSpec.describe 'Sign In' do
|
|||||||
fill_in 'Password', with: 'wrong_password'
|
fill_in 'Password', with: 'wrong_password'
|
||||||
click_button 'Sign In'
|
click_button 'Sign In'
|
||||||
|
|
||||||
expect(page).to have_content('Invalid Email or password')
|
expect(page).to have_content('Invalid email or password')
|
||||||
expect(page).not_to have_content('Document Templates')
|
expect(page).not_to have_content('Document Templates')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -51,7 +51,7 @@ RSpec.describe 'Sign In' do
|
|||||||
fill_in 'Two-Factor Code from Authenticator App', with: '123456'
|
fill_in 'Two-Factor Code from Authenticator App', with: '123456'
|
||||||
click_button 'Sign In'
|
click_button 'Sign In'
|
||||||
|
|
||||||
expect(page).to have_content('Invalid Email or password')
|
expect(page).to have_content('Invalid email or password')
|
||||||
expect(page).not_to have_content('Document Templates')
|
expect(page).not_to have_content('Document Templates')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user