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