lib/wolf_core/utils/file_utils.rb



require_relative "logging_utils"
require_relative "string_utils"

module WolfCore
  module FileUtils
    extend WolfCore::LoggingUtils
    extend WolfCore::StringUtils

    module_function

    def require_relative_folder(*folders)
      folder_files = File.join(*folders, "**", "*.rb")
      files_to_require = Dir[folder_files].sort
      # log_object files_to_require, title: 'Files to require are'
      safe_require(files_to_require)
    end

    def safe_require(missing_files) # rubocop:disable Metrics/MethodLength
      error_counter = {}
      while missing_files.any?
        files_to_require = missing_files
        missing_files = []
        files_to_require.each do |file|
          # log_object "Requiring file: #{file}"
          require_relative file
        rescue NameError => e
          error_counter[file] = error_counter[file].to_i + 1
          if error_counter[file] >= 10
            log_object "Error requiring file: #{file}"
            log_object e, title: "Error is"
          end
          missing_files << file if error_counter[file] < 15
        end
      end
    end

    def delete_files(*args)
      pattern = File.join(*args)
      files_to_delete = Dir.glob(pattern)
      files_to_delete.each do |file|
        File.delete(file)
        log_object "File deleted: #{file}"
      end

      log_object "File Deleting Process Finished! (#{files_to_delete.size} files deleted)"
    end

    def encode_file_to_base64_from_url(url) # rubocop:disable Metrics/MethodLength
      uri, response = download_file_from_url(url).values_at(:uri, :response)
      bytes = response.body

      encoded_file = Base64.strict_encode64(bytes)

      filename = filename_from_response(response, uri)
      log_object filename, title: "filename is"
      { filename: filename, encoded_file: encoded_file }
    rescue StandardError => e
      raise_service_error({
                            message: "Failed to encode file url",
                            encode_file_error: { message: e.message, backtrace: e.backtrace },
                            url: url
                          })
    end

    def download_file_from_url(url)
      uri = URI.parse(url)
      response = Net::HTTP.get_response(uri)
      raise_service_error({ message: "Failed to download file", url: url }) unless response.is_a?(Net::HTTPSuccess)
      { uri: uri, response: response }
    end

    def bytes_from_url(url)
      uri, response = download_file_from_url(url).values_at(:uri, :response)
      bytes = response.body
      filename = filename_from_response(response, uri)
      { bytes: bytes, filename: filename }
    end

    def filename_from_response(response, uri)
      content_disposition = response["content-disposition"]
      if content_disposition
        match = content_disposition.match(/filename="?([^"]+)"?/)
        match ? match[1] : File.basename(uri.path)
      else
        File.basename(uri.path)
      end
    end

    def generate_url_from_file(file:, api_key:)
      url = URI.parse("https://www.filestackapi.com/api/store/S3?key=#{api_key}")
      request = Net::HTTP::Post.new(url)
      request["Content-Type"] = "application/octet-stream"
      request.body = bytes_from_file(file)

      response = Net::HTTP.start(url.hostname, url.port, use_ssl: true) do |http|
        http.request(request)
      end

      parse_json_error = nil
      if response.is_a?(Net::HTTPSuccess)
        begin
          response_body = JSON.parse(response.body)
        rescue => e
          parse_json_error = {
            message: e.message,
            backtrace: e.backtrace,
          }
        end
        return response_body['url'] if response_body['url'].present?
      end

      raise_service_error({
        message: "Failed to generate url from file",
        url: url,
        file: file,
        request: request,
        response: response,
        response_body: response.body,
        parse_json_error: parse_json_error,
      })
    end

    def bytes_from_file(file)
      if file.is_a?(ActionDispatch::Http::UploadedFile)
        file.read
      elsif base64_encoded?(file)
        base64_string_to_bytes(file)
      else
        raise_service_error({
          message: "Can not generate bytes from this file",
          file: file,
        })
      end
    end

    def base64_string_to_bytes(base64_string)
      begin
        bytes = Base64.strict_decode64(base64_string)
        bytes
      rescue ArgumentError
        raise_service_error({ message: 'Failed to decode base64 string', base64_string: base64_string })
      end
    end
  end
end