Module: Daytona::Sdk::FileDownloadPatch

Defined in:
lib/daytona/sdk/file_download_patch.rb

Overview

rubocop:disable Metrics/AbcSize, Metrics/BlockLength, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity

Class Method Summary collapse

Class Method Details

.apply!(api_client_class, api_error_class) ⇒ Object



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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/daytona/sdk/file_download_patch.rb', line 32

def self.apply!(api_client_class, api_error_class)
  return if api_client_class.method_defined?(:_daytona_orig_call_api)

  api_client_class.class_eval do
    define_method(:_daytona_error_body_key) do
      @daytona_error_body_key ||= :"daytona_file_download_error_body_#{object_id}"
    end

    define_method(:download_file) do |request, &block|
      tempfile = nil
      encoding = nil
      stream_to_tempfile = false
      error_body = String.new.b
      Thread.current.thread_variable_set(_daytona_error_body_key, nil)

      request.on_headers do |response|
        stream_to_tempfile = response.code && response.code >= 200 && response.code < 300
        next unless stream_to_tempfile

        content_disposition = response.headers['Content-Disposition']
        if content_disposition && content_disposition =~ /filename=/i
          filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
          prefix = sanitize_filename(filename)
        else
          prefix = 'download-'
        end
        prefix += '-' unless prefix.end_with?('-')
        encoding = response.body.encoding
        tempfile = Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding)
      end

      request.on_body do |chunk|
        if stream_to_tempfile
          chunk.force_encoding(encoding)
          tempfile.write(chunk)
        else
          error_body << chunk.b
        end
      end

      request.on_complete do
        if stream_to_tempfile
          if tempfile.nil?
            raise api_error_class,
                  "Failed to create the tempfile based on the HTTP response from the server: #{request.inspect}"
          end
          tempfile.close
          @config.logger.info(
            "Temp file written to #{tempfile.path}, please copy the file to a proper folder " \
            "with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file " \
            'will be deleted automatically with GC. It\'s also recommended to delete the temp file ' \
            'explicitly with `tempfile.delete`'
          )
          block&.call(tempfile)
        else
          Thread.current.thread_variable_set(_daytona_error_body_key, error_body) unless error_body.empty?
        end
      end
    end

    alias_method :_daytona_orig_call_api, :call_api

    define_method(:call_api) do |http_method, path, opts = {}|
      _daytona_orig_call_api(http_method, path, opts)
    rescue api_error_class => e
      error_body = Thread.current.thread_variable_get(_daytona_error_body_key)
      if opts[:return_type] == 'File' && error_body && !error_body.empty?
        new_err = api_error_class.new(
          code: e.code,
          response_headers: e.response_headers,
          response_body: error_body
        )
        raise new_err, e.message
      end
      raise
    ensure
      Thread.current.thread_variable_set(_daytona_error_body_key, nil)
    end

    private :_daytona_error_body_key
  end
end