module Ecoportal module API class V2 class S3 class Upload class MissingFile < ArgumentError; end DEFAULT_MIME_TYPE = 'application/octet-stream'.freeze EXPECTED_CODE = "204".freeze # No Content MAX_RETRIES = 5 attr_reader :filename def initialize(credentials, file:) msg = "Expecting #{credentials_class}. Given: #{credentials.class}" raise ArgumentError, msg unless credentials.is_a?(credentials_class) @credentials = credentials @filename = file end def upload! require_deps! @response = Net::HTTP.start( uri.hostname, uri.port, use_ssl: true ) do |https| https.max_retries = self.class::MAX_RETRIES https.request(request) end return unless success? yield(s3_file_reference) if block_given? s3_file_reference end def failed? return false unless response response.code != EXPECTED_CODE end def success? return false unless response response.code == EXPECTED_CODE end def s3_file_reference return unless success? @s3_file_reference ||= { 'filename' => file_basename, 'filetype' => mime_type, 'filesize' => file_size, 'path' => s3_file_path } end private attr_reader :credentials attr_reader :response def request Net::HTTP::Post.new(uri).tap do |req| req.set_form form_data, 'multipart/form-data' end end def uri @uri ||= URI(credentials[:s3_endpoint]) end def form_data [ ['key', s3_file_path], *aws_params, *file_params ] end def s3_file_path "#{user_tmpdir}/#{file_basename}" end def user_tmpdir credentials[:user_tmpdir] end def aws_params params = %i[AWSAccessKeyId policy signature x-amz-server-side-encryption] params.map do |key| [key.to_s, credentials[key]] end end def file_params [ ['content-type', mime_type], ['file', io_stream] ] end def mime_type @mime_type ||= lib_mime_type || DEFAULT_MIME_TYPE end def lib_mime_type MIME::Types.type_for(file_extension).first&.content_type end def io_stream require_file! File.open(file_fullname) end def credentials_class Ecoportal::API::V2::S3::Data end def file_size @file_size ||= File.size(file_fullname) end def file_extension @file_extension ||= File.extname(filename)[1..] end def file_basename @file_basename ||= File.basename(filename) end def file_fullname @file_fullname ||= File.expand_path(filename) end def require_file! msg = "File '#{file_basename}' doesn't exist" raise MissingFile, msg unless File.exist?(file_fullname) end def require_deps! require 'net/http' require 'mime/types' end end end end end end