Class: Edi::Retriever
- Inherits:
-
BaseService
- Object
- BaseService
- Edi::Retriever
- Defined in:
- app/services/edi/retriever.rb
Overview
I connect to a transport, retrieve files and save them in the Edi Communication log
Direct Known Subclasses
Instance Attribute Summary
Attributes inherited from BaseService
Instance Method Summary collapse
- #instantiate_transporter(transporter, transporter_profile = nil) ⇒ Object
- #process(transporter: :sftp, transporter_profile: nil, remote_path:, file_pattern: nil, partner:, category:, data_type: 'xml', store_to_upload: false) ⇒ Object
Methods inherited from BaseService
#initialize, #log_debug, #log_error, #log_info, #log_warning, #logger, #tagged_logger
Constructor Details
This class inherits a constructor from BaseService
Instance Method Details
#instantiate_transporter(transporter, transporter_profile = nil) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'app/services/edi/retriever.rb', line 78 def instantiate_transporter(transporter, transporter_profile = nil) case transporter when :sftp Transport::SftpConnection.new(transporter_profile) when :basic_http_api Transport::BasicHttpApiConnection.new({ profile: transporter_profile }) when :http_api Transport::HttpApiConnection.new when :local_file Transport::LocalFile.new else raise "Unknown transporter: #{transporter}" end end |
#process(transporter: :sftp, transporter_profile: nil, remote_path:, file_pattern: nil, partner:, category:, data_type: 'xml', store_to_upload: false) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'app/services/edi/retriever.rb', line 5 def process(transporter: :sftp, transporter_profile: nil, # Transporter profile for sftp connection, see secrets.yml remote_path:, # Directory to pull from, usually specified in partner configuration file_pattern: nil, # Regexp file pattern to match partner:, # Name of partner for creating log entry category:, # Category for log entry data_type: 'xml', store_to_upload: false) # By default the file content is stored to the log directly, if true then an upload is created ) transport = instantiate_transporter(transporter, transporter_profile) batch_data = if store_to_upload transport.batch_download_to_file(remote_path, nil, file_pattern) else transport.batch_download_data(remote_path, file_pattern) end logger.warn "No new files found at #{remote_path} with pattern: #{file_pattern}" if batch_data.blank? edi_logs = [] batch_data.each do |file_name, data| remote_file_path = "#{remote_path}/#{file_name}" # Idempotency guard. A prior run may have committed this file but then # failed to delete it remotely (e.g. an SFTP blip *after* COMMIT, or the # process was killed between commit and rm). On the next pass we must not # create a duplicate log — recognise the already-stored file and just # clear the stale source copy. CommerceHub file names embed the batch # number + md5, so (partner, category, file_name) is unique per delivery. if (existing = EdiCommunicationLog.find_by(partner: partner, category: category, file_name: file_name)) logger.info "#{file_name} already stored as edi log #{existing.id}; removing stale remote copy" edi_logs << existing transport.rm(remote_file_path) next end edi_log = nil EdiCommunicationLog.transaction do data_blob = store_to_upload ? nil : data.encode("UTF-8", invalid: :replace, replace: "") # clean the data so our normalizr doesn't explode here on non-UTF-8 characters edi_log = EdiCommunicationLog.create! partner: partner, category: category, data: data_blob, data_type: data_type, file_name: file_name, transmit_datetime: Time.current if store_to_upload upload = Upload.uploadify(data, 'custom_packing_slip_pdf', edi_log, file_name) raise "Could not uploadify file #{file_name}" unless upload.persisted? end end # Delete the source file ONLY after the row is durably committed. Removing # it inside the transaction (the old behaviour) coupled the file's deletion # to an uncommitted insert: if COMMIT then failed — failover, pool timeout, # a SIGKILL during deploy — the insert rolled back while the file was # already gone from SFTP, losing the message with no trace on either side. # Re-running now re-fetches the file and the guard above keeps it idempotent. # # Belt-and-suspenders: re-read the row by its natural key before deleting # our only copy. A silent fake-success commit (a poisoned pooled connection # that returns without raising — even handing back another row's id) would # otherwise pass save! and we'd delete the source anyway. confirm_persisted! # raises if the row isn't really there, so the file stays for the next run. Durability.confirm_persisted!(EdiCommunicationLog, { partner: partner, category: category, file_name: file_name }, context: { remote_path: remote_path, edi_log_id: edi_log.id }) logger.info "#{file_name} saved to edi log #{edi_log.id}" edi_logs << edi_log transport.rm(remote_file_path) rescue StandardError => e ErrorReporting.error(e, "Problem during the processing of batch_data in file: #{file_name}") end edi_logs end |