mirror of https://github.com/docusealco/docuseal
				
				
				
			
							parent
							
								
									86d680cfdf
								
							
						
					
					
						commit
						04e7f101be
					
				| @ -0,0 +1,25 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | # == Schema Information | ||||||
|  | # | ||||||
|  | # Table name: lock_events | ||||||
|  | # | ||||||
|  | #  id         :bigint           not null, primary key | ||||||
|  | #  event_name :string           not null | ||||||
|  | #  key        :string           not null | ||||||
|  | #  created_at :datetime         not null | ||||||
|  | #  updated_at :datetime         not null | ||||||
|  | # | ||||||
|  | # Indexes | ||||||
|  | # | ||||||
|  | #  index_lock_events_on_event_name_and_key  (event_name,key) UNIQUE WHERE ((event_name)::text = ANY ((ARRAY['start'::character varying, 'complete'::character varying])::text[])) | ||||||
|  | #  index_lock_events_on_key                 (key) | ||||||
|  | # | ||||||
|  | class LockEvent < ApplicationRecord | ||||||
|  |   enum :event_name, { | ||||||
|  |     complete: 'complete', | ||||||
|  |     fail: 'fail', | ||||||
|  |     start: 'start', | ||||||
|  |     retry: 'retry' | ||||||
|  |   }, scope: false | ||||||
|  | end | ||||||
| @ -0,0 +1,14 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class CreateLockEvents < ActiveRecord::Migration[8.0] | ||||||
|  |   def change | ||||||
|  |     create_table :lock_events do |t| | ||||||
|  |       t.string :key, index: true, null: false | ||||||
|  |       t.string :event_name, null: false | ||||||
|  | 
 | ||||||
|  |       t.index %i[event_name key], unique: true, where: "event_name IN ('start', 'complete')" | ||||||
|  | 
 | ||||||
|  |       t.timestamps | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
| @ -0,0 +1,74 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | module Submissions | ||||||
|  |   module EnsureAuditGenerated | ||||||
|  |     WAIT_FOR_RETRY = 2.seconds | ||||||
|  |     CHECK_EVENT_INTERVAL = 1.second | ||||||
|  |     CHECK_COMPLETE_TIMEOUT = 90.seconds | ||||||
|  |     KEY_PREFIX = 'audit_trail' | ||||||
|  | 
 | ||||||
|  |     WaitForCompleteTimeout = Class.new(StandardError) | ||||||
|  |     NotCompletedYet = Class.new(StandardError) | ||||||
|  | 
 | ||||||
|  |     module_function | ||||||
|  | 
 | ||||||
|  |     def call(submission) | ||||||
|  |       return nil unless submission | ||||||
|  | 
 | ||||||
|  |       raise NotCompletedYet unless submission.submitters.all?(&:completed_at?) | ||||||
|  | 
 | ||||||
|  |       key = [KEY_PREFIX, submission.id].join(':') | ||||||
|  | 
 | ||||||
|  |       if ApplicationRecord.uncached { LockEvent.exists?(key:, event_name: :complete) } | ||||||
|  |         return submission.audit_trail_attachment | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       events = ApplicationRecord.uncached { LockEvent.where(key:).order(:id).to_a } | ||||||
|  | 
 | ||||||
|  |       if events.present? && events.last.event_name.in?(%w[start retry]) | ||||||
|  |         wait_for_complete_or_fail(submission) | ||||||
|  |       else | ||||||
|  |         LockEvent.create!(key:, event_name: events.present? ? :retry : :start) | ||||||
|  | 
 | ||||||
|  |         result = Submissions::GenerateAuditTrail.call(submission) | ||||||
|  | 
 | ||||||
|  |         LockEvent.create!(key:, event_name: :complete) | ||||||
|  | 
 | ||||||
|  |         result | ||||||
|  |       end | ||||||
|  |     rescue ActiveRecord::RecordNotUnique | ||||||
|  |       sleep WAIT_FOR_RETRY | ||||||
|  | 
 | ||||||
|  |       retry | ||||||
|  |     rescue StandardError => e | ||||||
|  |       Rollbar.error(e) if defined?(Rollbar) | ||||||
|  |       Rails.logger.error(e) | ||||||
|  | 
 | ||||||
|  |       LockEvent.create!(key:, event_name: :fail) | ||||||
|  | 
 | ||||||
|  |       raise | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     def wait_for_complete_or_fail(submission) | ||||||
|  |       total_wait_time = 0 | ||||||
|  | 
 | ||||||
|  |       loop do | ||||||
|  |         sleep CHECK_EVENT_INTERVAL | ||||||
|  |         total_wait_time += CHECK_EVENT_INTERVAL | ||||||
|  | 
 | ||||||
|  |         last_event = | ||||||
|  |           ApplicationRecord.uncached do | ||||||
|  |             LockEvent.where(key: [KEY_PREFIX, submission.id].join(':')).order(:id).last | ||||||
|  |           end | ||||||
|  | 
 | ||||||
|  |         if last_event.event_name.in?(%w[complete fail]) | ||||||
|  |           break ApplicationRecord.uncached do | ||||||
|  |             ActiveStorage::Attachment.find_by(record: submission, name: 'audit_trail') | ||||||
|  |           end | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         raise WaitForCompleteTimeout if total_wait_time > CHECK_COMPLETE_TIMEOUT | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
| @ -0,0 +1,74 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | module Submissions | ||||||
|  |   module EnsureCombinedGenerated | ||||||
|  |     WAIT_FOR_RETRY = 2.seconds | ||||||
|  |     CHECK_EVENT_INTERVAL = 1.second | ||||||
|  |     CHECK_COMPLETE_TIMEOUT = 90.seconds | ||||||
|  |     KEY_PREFIX = 'combined_document' | ||||||
|  | 
 | ||||||
|  |     WaitForCompleteTimeout = Class.new(StandardError) | ||||||
|  |     NotCompletedYet = Class.new(StandardError) | ||||||
|  | 
 | ||||||
|  |     module_function | ||||||
|  | 
 | ||||||
|  |     def call(submitter) | ||||||
|  |       return nil unless submitter | ||||||
|  | 
 | ||||||
|  |       raise NotCompletedYet unless submitter.completed_at? | ||||||
|  | 
 | ||||||
|  |       key = [KEY_PREFIX, submitter.id].join(':') | ||||||
|  | 
 | ||||||
|  |       if ApplicationRecord.uncached { LockEvent.exists?(key:, event_name: :complete) } | ||||||
|  |         return submitter.submission.combined_document_attachment | ||||||
|  |       end | ||||||
|  | 
 | ||||||
|  |       events = ApplicationRecord.uncached { LockEvent.where(key:).order(:id).to_a } | ||||||
|  | 
 | ||||||
|  |       if events.present? && events.last.event_name.in?(%w[start retry]) | ||||||
|  |         wait_for_complete_or_fail(submitter) | ||||||
|  |       else | ||||||
|  |         LockEvent.create!(key:, event_name: events.present? ? :retry : :start) | ||||||
|  | 
 | ||||||
|  |         result = Submissions::GenerateCombinedAttachment.call(submitter) | ||||||
|  | 
 | ||||||
|  |         LockEvent.create!(key:, event_name: :complete) | ||||||
|  | 
 | ||||||
|  |         result | ||||||
|  |       end | ||||||
|  |     rescue ActiveRecord::RecordNotUnique | ||||||
|  |       sleep WAIT_FOR_RETRY | ||||||
|  | 
 | ||||||
|  |       retry | ||||||
|  |     rescue StandardError => e | ||||||
|  |       Rollbar.error(e) if defined?(Rollbar) | ||||||
|  |       Rails.logger.error(e) | ||||||
|  | 
 | ||||||
|  |       LockEvent.create!(key:, event_name: :fail) | ||||||
|  | 
 | ||||||
|  |       raise | ||||||
|  |     end | ||||||
|  | 
 | ||||||
|  |     def wait_for_complete_or_fail(submitter) | ||||||
|  |       total_wait_time = 0 | ||||||
|  | 
 | ||||||
|  |       loop do | ||||||
|  |         sleep CHECK_EVENT_INTERVAL | ||||||
|  |         total_wait_time += CHECK_EVENT_INTERVAL | ||||||
|  | 
 | ||||||
|  |         last_event = | ||||||
|  |           ApplicationRecord.uncached do | ||||||
|  |             LockEvent.where(key: [KEY_PREFIX, submitter.id].join(':')).order(:id).last | ||||||
|  |           end | ||||||
|  | 
 | ||||||
|  |         if last_event.event_name.in?(%w[complete fail]) | ||||||
|  |           break ApplicationRecord.uncached do | ||||||
|  |             ActiveStorage::Attachment.find_by(record: submitter.submission, name: 'combined_document') | ||||||
|  |           end | ||||||
|  |         end | ||||||
|  | 
 | ||||||
|  |         raise WaitForCompleteTimeout if total_wait_time > CHECK_COMPLETE_TIMEOUT | ||||||
|  |       end | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | end | ||||||
					Loading…
					
					
				
		Reference in new issue