require 'fileutils' require 'uri' begin require 'iostreams' rescue LoadError # Optional dependency end module RocketJob module Jobs # Job to upload a file into another job. # # Intended for use by DirmonJob to upload a file into a specified job. # # Can be used directly for any job class, as long as that job responds # to `#upload`. class UploadFileJob < RocketJob::Job self.priority = 30 # Name of the job class to instantiate and upload the file into. field :job_class_name, type: String, user_editable: true # Properties to assign to the job when it is created. field :properties, type: Hash, default: {}, user_editable: true # File to upload field :upload_file_name, type: String, user_editable: true # The original Input file name. # Used by #upload to extract the IOStreams when present. field :original_file_name, type: String, user_editable: true # Optionally set the job id for the downstream job # Useful when for example the archived file should contain the job id for the downstream job. field :job_id, type: BSON::ObjectId validates_presence_of :upload_file_name, :job_class_name validate :job_is_a_rocket_job validate :job_implements_upload validate :file_exists # Create the job and upload the file into it. def perform job = job_class.new(properties) job.id = job_id if job_id upload_file(job) job.save! rescue StandardError => exc # Prevent partial uploads job&.cleanup! if job.respond_to?(:cleanup!) raise(exc) end private def job_class @job_class ||= job_class_name.constantize rescue NameError nil end def upload_file(job) if job.respond_to?(:upload) if original_file_name job.upload(upload_file_name, file_name: original_file_name) else job.upload(upload_file_name) end elsif job.respond_to?(:upload_file_name=) job.upload_file_name = upload_file_name elsif job.respond_to?(:full_file_name=) job.full_file_name = upload_file_name else raise(ArgumentError, "Model #{job_class_name} must implement '#upload', or have attribute 'upload_file_name' or 'full_file_name'") end end # Validates job_class is a Rocket Job def job_is_a_rocket_job klass = job_class return if klass.nil? || klass.ancestors&.include?(RocketJob::Job) errors.add(:job_class_name, "Model #{job_class_name} must be defined and inherit from RocketJob::Job") end VALID_INSTANCE_METHODS = %i[upload upload_file_name= full_file_name=].freeze # Validates job_class is a Rocket Job def job_implements_upload klass = job_class return if klass.nil? || klass.instance_methods.any? { |m| VALID_INSTANCE_METHODS.include?(m) } errors.add(:job_class_name, "#{job_class} must implement any one of: :#{VALID_INSTANCE_METHODS.join(' :')} instance methods") end def file_exists return if upload_file_name.nil? uri = URI.parse(upload_file_name) return unless uri.scheme.nil? || uri.scheme == 'file' return if File.exist?(upload_file_name) errors.add(:upload_file_name, "Upload file: #{upload_file_name} does not exist.") end end end end