mirror of https://github.com/docusealco/docuseal
Merge pull request #39 from docusealco/wip
commit
fed420cae1
@ -0,0 +1,45 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
class SubmissionDocumentsController < ApiBaseController
|
||||
load_and_authorize_resource :submission
|
||||
|
||||
def index
|
||||
documents =
|
||||
if @submission.submitters.all?(&:completed_at?)
|
||||
last_submitter = @submission.submitters.max_by(&:completed_at)
|
||||
|
||||
if last_submitter.documents_attachments.blank?
|
||||
last_submitter.documents_attachments = Submissions::EnsureResultGenerated.call(submitter)
|
||||
end
|
||||
|
||||
last_submitter.documents_attachments
|
||||
else
|
||||
values_hash = Submissions::GeneratePreviewAttachments.build_values_hash(@submission)
|
||||
|
||||
if @submission.preview_documents.present? &&
|
||||
@submission.preview_documents.all? { |s| s.metadata['values_hash'] == values_hash }
|
||||
@submission.preview_documents
|
||||
else
|
||||
ApplicationRecord.no_touching do
|
||||
@submission.preview_documents.each(&:destroy)
|
||||
end
|
||||
|
||||
Submissions::GeneratePreviewAttachments.call(@submission, values_hash:)
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Associations::Preloader.new(
|
||||
records: documents,
|
||||
associations: [:blob]
|
||||
).call
|
||||
|
||||
render json: {
|
||||
id: @submission.id,
|
||||
documents: documents.map do |attachment|
|
||||
{ name: attachment.filename.base, url: ActiveStorage::Blob.proxy_url(attachment.blob) }
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,96 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Submissions
|
||||
module GeneratePreviewAttachments
|
||||
module_function
|
||||
|
||||
# rubocop:disable Metrics
|
||||
def call(submission, values_hash: nil)
|
||||
values_hash ||= build_values_hash(submission)
|
||||
|
||||
with_signature_id = submission.account.account_configs
|
||||
.exists?(key: AccountConfig::WITH_SIGNATURE_ID, value: true)
|
||||
|
||||
is_flatten =
|
||||
submission.account.account_configs
|
||||
.find_or_initialize_by(key: AccountConfig::FLATTEN_RESULT_PDF_KEY).value != false
|
||||
|
||||
pdfs_index = GenerateResultAttachments.build_pdfs_index(submission, flatten: is_flatten)
|
||||
|
||||
submission.submitters.where(completed_at: nil).preload(attachments_attachments: :blob).each do |submitter|
|
||||
GenerateResultAttachments.fill_submitter_fields(submitter, submission.account, pdfs_index,
|
||||
with_signature_id:, is_flatten:)
|
||||
end
|
||||
|
||||
template = submission.template
|
||||
|
||||
image_pdfs = []
|
||||
original_documents = template.documents.preload(:blob)
|
||||
|
||||
result_attachments =
|
||||
(submission.template_schema || template.schema).map do |item|
|
||||
pdf = pdfs_index[item['attachment_uuid']]
|
||||
|
||||
if original_documents.find { |a| a.uuid == item['attachment_uuid'] }.image?
|
||||
pdf = GenerateResultAttachments.normalize_image_pdf(pdf)
|
||||
|
||||
image_pdfs << pdf
|
||||
end
|
||||
|
||||
build_pdf_attachment(pdf:, submission:,
|
||||
uuid: item['attachment_uuid'],
|
||||
values_hash:,
|
||||
name: item['name'])
|
||||
end
|
||||
|
||||
return ApplicationRecord.no_touching { result_attachments.map { |e| e.tap(&:save!) } } if image_pdfs.size < 2
|
||||
|
||||
images_pdf =
|
||||
image_pdfs.each_with_object(HexaPDF::Document.new) do |pdf, doc|
|
||||
pdf.pages.each { |page| doc.pages << doc.import(page) }
|
||||
end
|
||||
|
||||
images_pdf = GenerateResultAttachments.normalize_image_pdf(images_pdf)
|
||||
|
||||
images_pdf_attachment =
|
||||
build_pdf_attachment(
|
||||
pdf: images_pdf,
|
||||
submission:,
|
||||
uuid: GenerateResultAttachments.images_pdf_uuid(original_documents.select(&:image?)),
|
||||
values_hash:,
|
||||
name: template.name
|
||||
)
|
||||
|
||||
ApplicationRecord.no_touching do
|
||||
(result_attachments + [images_pdf_attachment]).map { |e| e.tap(&:save!) }
|
||||
end
|
||||
end
|
||||
|
||||
def build_values_hash(submission)
|
||||
submission.submitters.reduce({}) { |acc, s| acc.merge(s.values) }.hash
|
||||
end
|
||||
|
||||
def build_pdf_attachment(pdf:, submission:, uuid:, name:, values_hash:)
|
||||
io = StringIO.new
|
||||
|
||||
begin
|
||||
pdf.write(io, incremental: true, validate: false)
|
||||
rescue HexaPDF::MalformedPDFError => e
|
||||
Rollbar.error(e) if defined?(Rollbar)
|
||||
|
||||
pdf.write(io, incremental: false, validate: false)
|
||||
end
|
||||
|
||||
ActiveStorage::Attachment.new(
|
||||
blob: ActiveStorage::Blob.create_and_upload!(io: io.tap(&:rewind), filename: "#{name}.pdf"),
|
||||
metadata: { original_uuid: uuid,
|
||||
values_hash:,
|
||||
analyzed: true,
|
||||
sha256: Base64.urlsafe_encode64(Digest::SHA256.digest(io.string)) },
|
||||
name: 'preview_documents',
|
||||
record: submission
|
||||
)
|
||||
end
|
||||
# rubocop:enable Metrics
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,139 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Params::BaseValidator do
|
||||
let(:validator) { described_class.new({}) }
|
||||
|
||||
describe '#email_format' do
|
||||
it 'when email is valid' do
|
||||
emails = [
|
||||
' john.doe@example.com ',
|
||||
'john.doe@example.com',
|
||||
'jane+newsletter@domain.org',
|
||||
'mike_smith@company.net',
|
||||
'lisa-wong@sub.example.co.uk',
|
||||
'peter@webmail.com',
|
||||
'anna.jones123@my-domain.com',
|
||||
'contact@company.email',
|
||||
'info@my-company123.org',
|
||||
'hello.world@business.info',
|
||||
'feedback@new-domain.com',
|
||||
'alerts+user@localdomain.net',
|
||||
'webmaster@industry.biz',
|
||||
'services@agency.example',
|
||||
'george123@consultant.pro',
|
||||
'sales-team@company.io'
|
||||
]
|
||||
|
||||
emails.each do |email|
|
||||
expect { validator.email_format({ email: }, :email) }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
it 'when signle email is invalid' do
|
||||
emails = [
|
||||
'jone.doe@',
|
||||
'mike.smith@',
|
||||
'jane.doe@@example.com',
|
||||
'@example.com',
|
||||
'lisa.wong@example',
|
||||
'peter.parker..@example.com',
|
||||
'anna.jones@.com',
|
||||
'jack.brown@com',
|
||||
'john doe@example.com',
|
||||
'laura.martin@ example.com',
|
||||
'dave.clark@example .com',
|
||||
'susan.green@example,com',
|
||||
'chris.lee@example;com',
|
||||
'jenny.king@.example.com',
|
||||
'.henry.ford@example.com',
|
||||
'amy.baker@sub_domain.com',
|
||||
'george.morris@-example.com',
|
||||
'nancy.davis@example..com',
|
||||
'kevin.white@.',
|
||||
'diana.robinson@.example..com',
|
||||
'oliver.scott@example.c',
|
||||
'email1@g.comemail@g.com',
|
||||
'user.name@subdomain.example@example.com',
|
||||
'double@at@sign.com',
|
||||
'user@@example.com',
|
||||
'email@123.123.123.123',
|
||||
'this...is@strange.but.valid.com',
|
||||
'mix-and.match@strangely-formed-email_address.com',
|
||||
'email@domain..com',
|
||||
'user@-weird-domain-.com',
|
||||
'user.name@[IPv6:2001:db8::1]',
|
||||
'tricky.email@sub.example-.com',
|
||||
'user@domain.c0m'
|
||||
]
|
||||
|
||||
emails.each do |email|
|
||||
expect do
|
||||
validator.email_format({ email: }, :email)
|
||||
end.to raise_error(described_class::InvalidParameterError, 'email must follow the email format')
|
||||
end
|
||||
end
|
||||
|
||||
it 'when multiple emails are valid' do
|
||||
emails = [
|
||||
|
||||
'john.doe@example.com, jane.doe+newsletter@domain.org',
|
||||
'joshua@automobile.car ; chloe+fashion@food.delivery',
|
||||
'mike-smith@company.net;lisa.wong-sales@sub.example.co.uk',
|
||||
'peter.parker+info@webmail.com,laura.martin-office@company.co',
|
||||
'anna.jones123@my-domain.com, jack.brown+work@college.edu',
|
||||
'susan.green@business-info.org; dave.clark+personal@nonprofit.org',
|
||||
'chris.lee+team@new-domain.com;jenny.king.marketing@localdomain.net',
|
||||
'george.morris@consultant.pro; nancy.davis-office@company.io',
|
||||
'joshua-jones@automobile.car; chloe.taylor+fashion@food.delivery',
|
||||
'ryan.moore+alerts@music-band.com,isabella.walker.design@fashion.design',
|
||||
'support-team@company.com, contact.us@domain.org',
|
||||
'admin.office@industry.biz, hr.department@service.pro',
|
||||
'feedback@agency-example.org; hello.world@creative-studio.net',
|
||||
'sales-team@e-commerce.shop, support.department@technology.co',
|
||||
'media.contact@financial.servicesl; events-coordinator@food.delivery',
|
||||
'order@music-band.com; info.support@creative.example',
|
||||
'design.team@webmail.com , admin-office@company.co',
|
||||
'contact.sales@sub-example.co.uk, support+info@legal.gov',
|
||||
'support@media.group;subscribe-updates@concert.events'
|
||||
]
|
||||
|
||||
emails.each do |email|
|
||||
expect { validator.email_format({ email: }, :email) }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
it 'when multiple emails are invalid' do
|
||||
emails = [
|
||||
'jone@gmail.com, ,mike@gmail.com',
|
||||
'john.doe@example.com dave@nonprofit.org',
|
||||
'; oliver.scott@example.com',
|
||||
'amy.baker@ example.com, george.morris@ example.com',
|
||||
'jenny.king@example.com . diana.robinson@example.com',
|
||||
'nancy.davis@.com, henry.ford@.com',
|
||||
'jack.brown@example.com, laura.martin@example .com',
|
||||
'anna.jones@example,com lisa.wong@example.com',
|
||||
'dave.clark@example.com kevin.white@example;com',
|
||||
'susan.green@ example.com; john.doe@example.com',
|
||||
'amy.baker@sub_domain.com george.morris@-example.com',
|
||||
'nancy.davis@example..com john.doe@example.c',
|
||||
'peter.parker@example.com, .henry.ford@example.com',
|
||||
'diana.robinson@.example..com, mike.smith@.',
|
||||
'oliver.scott@example.com; laura.martin@ example.com, jane.doe@@example.com'
|
||||
]
|
||||
|
||||
emails.each do |email|
|
||||
expect do
|
||||
validator.email_format({ email: }, :email)
|
||||
end.to raise_error(described_class::InvalidParameterError, 'email must follow the email format')
|
||||
end
|
||||
end
|
||||
|
||||
it 'when email is invalid with custom message' do
|
||||
expect do
|
||||
validator.email_format({ email: 'jone.doe@' }, :email, message: 'email is invalid')
|
||||
end.to raise_error(described_class::InvalidParameterError, 'email is invalid')
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in new issue