filestack 2.7.0 → 2.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_dependency "typhoeus", "~> 1.1"
26
26
  spec.add_dependency "parallel", "~> 1.11", ">= 1.11.2"
27
- spec.add_dependency "mimemagic", "~> 0.3.2"
27
+ spec.add_dependency "mini_mime", "~> 1.0.2"
28
28
  spec.add_dependency "progress_bar"
29
29
 
30
30
  spec.add_development_dependency "bundler", "~> 1.7"
@@ -22,6 +22,8 @@ class FilestackConfig
22
22
  'Accept-Encoding' => "application/json"
23
23
  }.freeze
24
24
 
25
+ DEFAULT_UPLOAD_MIMETYPE = 'application/octet-stream'
26
+
25
27
  INTELLIGENT_ERROR_MESSAGES = ['BACKEND_SERVER', 'BACKEND_NETWORK', 'S3_SERVER', 'S3_NETWORK']
26
28
 
27
29
  def self.multipart_start_url
@@ -1,7 +1,7 @@
1
1
  require 'filestack/config'
2
2
  require 'filestack/utils/utils'
3
3
  require 'filestack/utils/multipart_upload_utils'
4
- require 'mimemagic'
4
+ require 'mini_mime'
5
5
  require 'json'
6
6
 
7
7
  # Module is mixin for common functionalities that all Filestack
@@ -58,7 +58,7 @@ module FilestackCommon
58
58
  return 'Overwrite requires security' if security.nil?
59
59
 
60
60
  file = File.open(filepath, 'r')
61
- mimetype = MimeMagic.by_magic(file)
61
+ mimetype = MiniMime.lookup_by_filename(file).content_type
62
62
  content = file.read
63
63
 
64
64
  signature = security.signature
@@ -23,23 +23,25 @@ class FilestackClient
23
23
  @security = security
24
24
  end
25
25
 
26
- # Upload a local file or external url
26
+ # Upload a local file, external url or IO object
27
27
  # @param [String] filepath The path of a local file
28
28
  # @param [String] external_url An external URL
29
+ # @param [StringIO] io The IO object
29
30
  # @param [Hash] options User-supplied upload options
31
+ # @param [Boolean] intelligent Upload file using Filestack Intelligent Ingestion
32
+ # @param [String] storage Default storage to be used for uploads
30
33
  #
31
34
  # return [Filestack::FilestackFilelink]
32
- def upload(filepath: nil, external_url: nil, options: {}, intelligent: false, timeout: 60, storage: 'S3')
33
- return 'You cannot upload a URL and file at the same time' if filepath && external_url
35
+ def upload(filepath: nil, external_url: nil, io: nil, options: {}, intelligent: false, timeout: 60, storage: 'S3')
36
+ return 'You cannot upload a URL and file at the same time' if (filepath || io) && external_url
34
37
 
35
- response = if filepath
36
- multipart_upload(@apikey, filepath, @security, options, timeout, storage, intelligent: intelligent)
38
+ response = if external_url
39
+ send_upload(@apikey, external_url, @security, options)
37
40
  else
38
- send_upload(@apikey,
39
- external_url: external_url,
40
- options: options,
41
- security: @security)
41
+ return 'You cannot upload IO object and file at the same time' if io && filepath
42
+ multipart_upload(@apikey, filepath, io, @security, options, timeout, storage, intelligent)
42
43
  end
44
+
43
45
  FilestackFilelink.new(response['handle'], security: @security, apikey: @apikey)
44
46
  end
45
47
  # Transform an external URL
@@ -1,5 +1,5 @@
1
1
  module Filestack
2
2
  module Ruby
3
- VERSION = '2.7.0'.freeze
3
+ VERSION = '2.9.2'.freeze
4
4
  end
5
5
  end
@@ -1,7 +1,7 @@
1
1
  require 'base64'
2
2
  require 'timeout'
3
3
  require 'digest'
4
- require 'mimemagic'
4
+ require 'mini_mime'
5
5
  require 'json'
6
6
  require 'parallel'
7
7
  require 'typhoeus'
@@ -13,13 +13,22 @@ include UploadUtils
13
13
  include IntelligentUtils
14
14
  # Includes all the utility functions for Filestack multipart uploads
15
15
  module MultipartUploadUtils
16
- def get_file_info(file)
17
- filename = File.basename(file)
16
+
17
+ def get_file_attributes(file, options = {})
18
+ filename = options[:filename] || File.basename(file)
19
+ mimetype = options[:mimetype] || MiniMime.lookup_by_filename(File.open(file)).content_type || FilestackConfig::DEFAULT_UPLOAD_MIMETYPE
18
20
  filesize = File.size(file)
19
- mimetype = MimeMagic.by_magic(File.open(file))
20
- if mimetype.nil?
21
- mimetype = 'application/octet-stream'
22
- end
21
+
22
+ [filename, filesize, mimetype.to_s]
23
+ end
24
+
25
+ def get_io_attributes(io, options = {})
26
+ filename = options[:filename] || 'unnamed_file'
27
+ mimetype = options[:mimetype] || FilestackConfig::DEFAULT_UPLOAD_MIMETYPE
28
+
29
+ io.seek(0, IO::SEEK_END)
30
+ filesize = io.tell
31
+
23
32
  [filename, filesize, mimetype.to_s]
24
33
  end
25
34
 
@@ -31,8 +40,10 @@ module MultipartUploadUtils
31
40
  # @param [String] mimetype Mimetype of incoming file
32
41
  # @param [FilestackSecurity] security Security object with
33
42
  # policy/signature
43
+ # @param [String] storage Default storage to be used for uploads
34
44
  # @param [Hash] options User-defined options for
35
45
  # multipart uploads
46
+ # @param [Bool] intelligent Upload file using Filestack Intelligent Ingestion
36
47
  #
37
48
  # @return [Typhoeus::Response]
38
49
  def multipart_start(apikey, filename, filesize, mimetype, security, storage, options = {}, intelligent)
@@ -67,22 +78,21 @@ module MultipartUploadUtils
67
78
  #
68
79
  # @param [String] apikey Filestack API key
69
80
  # @param [String] filename Name of incoming file
70
- # @param [String] filepath Local path to file
71
81
  # @param [Int] filesize Size of incoming file
72
82
  # @param [Typhoeus::Response] start_response Response body from
73
83
  # multipart_start
84
+ # @param [String] storage Default storage to be used for uploads
74
85
  # @param [Hash] options User-defined options for
75
86
  # multipart uploads
76
87
  #
77
88
  # @return [Array]
78
- def create_upload_jobs(apikey, filename, filepath, filesize, start_response, storage, options)
89
+ def create_upload_jobs(apikey, filename, filesize, start_response, storage, options)
79
90
  jobs = []
80
91
  part = 1
81
92
  seek_point = 0
82
93
  while seek_point < filesize
83
94
  part_info = {
84
95
  seek_point: seek_point,
85
- filepath: filepath,
86
96
  filename: filename,
87
97
  apikey: apikey,
88
98
  part: part,
@@ -92,7 +102,7 @@ module MultipartUploadUtils
92
102
  upload_id: start_response['upload_id'],
93
103
  location_url: start_response['location_url'],
94
104
  start_response: start_response,
95
- store: { location: storage }
105
+ store: { location: storage },
96
106
  }
97
107
 
98
108
  part_info[:store].merge!(options) if options
@@ -116,15 +126,16 @@ module MultipartUploadUtils
116
126
  # @param [Hash] job Hash of options needed
117
127
  # to upload a chunk
118
128
  # @param [String] apikey Filestack API key
119
- # @param [String] location_url Location url given back
129
+ # @param [String] filepath Location url given back
120
130
  # from endpoint
121
- # @param [String] filepath Local path to file
131
+ # @param [StringIO] io The IO object
122
132
  # @param [Hash] options User-defined options for
123
133
  # multipart uploads
134
+ # @param [String] storage Default storage to be used for uploads
124
135
  #
125
136
  # @return [Typhoeus::Response]
126
- def upload_chunk(job, apikey, filepath, options, storage)
127
- file = File.open(filepath)
137
+ def upload_chunk(job, apikey, filepath, io, options, storage)
138
+ file = filepath ? File.open(filepath) : io
128
139
  file.seek(job[:seek_point])
129
140
  chunk = file.read(FilestackConfig::DEFAULT_CHUNK_SIZE)
130
141
 
@@ -139,7 +150,6 @@ module MultipartUploadUtils
139
150
  region: job[:region],
140
151
  upload_id: job[:upload_id],
141
152
  store: { location: storage },
142
- file: Tempfile.new(job[:filename])
143
153
  }
144
154
  data = data.merge!(options) if options
145
155
 
@@ -158,13 +168,14 @@ module MultipartUploadUtils
158
168
  # @param [String] filepath Local path to file
159
169
  # @param [Hash] options User-defined options for
160
170
  # multipart uploads
171
+ # @param [String] storage Default storage to be used for uploads
161
172
  #
162
173
  # @return [Array] Array of parts/etags strings
163
- def run_uploads(jobs, apikey, filepath, options, storage)
174
+ def run_uploads(jobs, apikey, filepath, io, options, storage)
164
175
  bar = ProgressBar.new(jobs.length)
165
176
  results = Parallel.map(jobs, in_threads: 4) do |job|
166
177
  response = upload_chunk(
167
- job, apikey, filepath, options, storage
178
+ job, apikey, filepath, io, options, storage
168
179
  )
169
180
  if response.code == 200
170
181
  bar.increment!
@@ -190,6 +201,8 @@ module MultipartUploadUtils
190
201
  # part numbers
191
202
  # @param [Hash] options User-defined options for
192
203
  # multipart uploads
204
+ # @param [String] storage Default storage to be used for uploads
205
+ # @param [Boolean] intelligent Upload file using Filestack Intelligent Ingestion
193
206
  #
194
207
  # @return [Typhoeus::Response]
195
208
  def multipart_complete(apikey, filename, filesize, mimetype, start_response, parts_and_etags, options, storage, intelligent = false)
@@ -215,32 +228,39 @@ module MultipartUploadUtils
215
228
  #
216
229
  # @param [String] apikey Filestack API key
217
230
  # @param [String] filename Name of incoming file
231
+ # @param [StringIO] io The IO object
218
232
  # @param [FilestackSecurity] security Security object with
219
233
  # policy/signature
220
234
  # @param [Hash] options User-defined options for
221
235
  # multipart uploads
236
+ # @param [String] storage Default storage to be used for uploads
237
+ # @param [Boolean] intelligent Upload file using Filestack Intelligent Ingestion
222
238
  #
223
239
  # @return [Hash]
224
- def multipart_upload(apikey, filepath, security, options, timeout, storage, intelligent: false)
225
- filename, filesize, mimetype = get_file_info(filepath)
240
+ def multipart_upload(apikey, filepath, io, security, options, timeout, storage, intelligent = false)
241
+ filename, filesize, mimetype = if filepath
242
+ get_file_attributes(filepath, options)
243
+ else
244
+ get_io_attributes(io, options)
245
+ end
226
246
 
227
247
  start_response = multipart_start(
228
248
  apikey, filename, filesize, mimetype, security, storage, options, intelligent
229
249
  )
230
250
 
231
251
  jobs = create_upload_jobs(
232
- apikey, filename, filepath, filesize, start_response, storage, options
252
+ apikey, filename, filesize, start_response, storage, options
233
253
  )
234
254
 
235
255
  if intelligent
236
256
  state = IntelligentState.new
237
- run_intelligent_upload_flow(jobs, state, storage)
257
+ run_intelligent_upload_flow(jobs, filepath, io, state, storage)
238
258
  response_complete = multipart_complete(
239
259
  apikey, filename, filesize, mimetype,
240
260
  start_response, nil, options, storage, intelligent
241
261
  )
242
262
  else
243
- parts_and_etags = run_uploads(jobs, apikey, filepath, options, storage)
263
+ parts_and_etags = run_uploads(jobs, apikey, filepath, io, options, storage)
244
264
  response_complete = multipart_complete(
245
265
  apikey, filename, filesize, mimetype,
246
266
  start_response, parts_and_etags, options, storage
@@ -1,7 +1,7 @@
1
1
  require 'base64'
2
2
  require 'digest'
3
3
  require 'fiber'
4
- require 'mimemagic'
4
+ require 'mini_mime'
5
5
  require 'json'
6
6
  require 'typhoeus'
7
7
 
@@ -63,7 +63,7 @@ module UploadUtils
63
63
  end
64
64
 
65
65
  def build_store_task(options = {})
66
- return 'store' if options.empty?
66
+ return 'store' if options.nil? || options.empty?
67
67
  tasks = []
68
68
  options.each do |key, value|
69
69
  value = case key
@@ -88,7 +88,7 @@ module UploadUtils
88
88
  # @param [Hash] options User-defined options for
89
89
  # multipart uploads
90
90
  # @return [Hash]
91
- def send_upload(apikey, external_url: nil, security: nil, options: nil)
91
+ def send_upload(apikey, external_url = nil, security = nil, options = nil)
92
92
  base = "#{FilestackConfig::CDN_URL}/#{apikey}/#{build_store_task(options)}"
93
93
 
94
94
  if security
@@ -100,9 +100,13 @@ module UploadUtils
100
100
  response = Typhoeus.post("#{base}/#{external_url}", headers: FilestackConfig::HEADERS)
101
101
 
102
102
  if response.code == 200
103
- response_body = JSON.parse(response.body)
104
- handle = response_body['url'].split('/').last
105
- return { 'handle' => handle }
103
+ begin
104
+ response_body = JSON.parse(response.body)
105
+ handle = response_body['url'].split('/').last
106
+ return { 'handle' => handle }
107
+ rescue
108
+ raise response.body
109
+ end
106
110
  end
107
111
  raise response.body
108
112
  end
@@ -199,7 +203,7 @@ module IntelligentUtils
199
203
  # @param [IntelligentState] state An IntelligentState object
200
204
  #
201
205
  # @return [Array]
202
- def run_intelligent_upload_flow(jobs, state, storage)
206
+ def run_intelligent_upload_flow(jobs, filepath, io, state, storage)
203
207
  bar = ProgressBar.new(jobs.length)
204
208
  generator = create_intelligent_generator(jobs)
205
209
  working_offset = FilestackConfig::DEFAULT_OFFSET_SIZE
@@ -207,7 +211,7 @@ module IntelligentUtils
207
211
  batch = get_generator_batch(generator)
208
212
  # run parts
209
213
  Parallel.map(batch, in_threads: 4) do |part|
210
- state = run_intelligent_uploads(part, state, storage)
214
+ state = run_intelligent_uploads(part, filepath, io, state, storage)
211
215
  # condition: a chunk has failed but we have not reached the maximum retries
212
216
  while bad_state(state)
213
217
  # condition: timeout to S3, requiring offset size to be changed
@@ -219,7 +223,7 @@ module IntelligentUtils
219
223
  sleep(state.backoff)
220
224
  end
221
225
  state.add_retry
222
- state = run_intelligent_uploads(part, state, storage)
226
+ state = run_intelligent_uploads(part, filepath, io, state, storage)
223
227
  end
224
228
  raise "Upload has failed. Please try again later." unless state.ok
225
229
  bar.increment!
@@ -275,7 +279,7 @@ module IntelligentUtils
275
279
  # multipart_start
276
280
  #
277
281
  # @return [Dict]
278
- def chunk_job(job, state, apikey, filename, filepath, filesize, start_response, storage)
282
+ def chunk_job(job, state, apikey, filename, filesize, start_response, storage)
279
283
  offset = 0
280
284
  seek_point = job[:seek_point]
281
285
  chunk_list = []
@@ -283,7 +287,6 @@ module IntelligentUtils
283
287
  while (offset < FilestackConfig::DEFAULT_CHUNK_SIZE) && (seek_point + offset) < filesize
284
288
  chunk_list.push(
285
289
  seek_point: seek_point,
286
- filepath: filepath,
287
290
  filename: filename,
288
291
  apikey: apikey,
289
292
  part: job[:part],
@@ -307,15 +310,14 @@ module IntelligentUtils
307
310
  # @param [IntelligentState] state An IntelligentState object
308
311
  #
309
312
  # @return [IntelligentState]
310
- def run_intelligent_uploads(part, state, storage)
313
+ def run_intelligent_uploads(part, filepath, io, state, storage)
311
314
  failed = false
312
315
  chunks = chunk_job(
313
- part, state, part[:apikey], part[:filename], part[:filepath],
314
- part[:filesize], part[:start_response], storage
316
+ part, state, part[:apikey], part[:filename], part[:filesize], part[:start_response], storage
315
317
  )
316
318
  Parallel.map(chunks, in_threads: 3) do |chunk|
317
319
  begin
318
- upload_chunk_intelligently(chunk, state, part[:apikey], part[:filepath], part[:options], storage)
320
+ upload_chunk_intelligently(chunk, state, part[:apikey], filepath, io, part[:options], storage)
319
321
  rescue => e
320
322
  state.error_type = e.message
321
323
  failed = true
@@ -364,8 +366,8 @@ module IntelligentUtils
364
366
  # multipart uploads
365
367
  #
366
368
  # @return [Typhoeus::Response]
367
- def upload_chunk_intelligently(job, state, apikey, filepath, options, storage)
368
- file = File.open(filepath)
369
+ def upload_chunk_intelligently(job, state, apikey, filepath, io, options, storage)
370
+ file = filepath ? File.open(filepath) : io
369
371
  file.seek(job[:seek_point] + job[:offset])
370
372
 
371
373
  chunk = file.read(state.offset)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: filestack
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.0
4
+ version: 2.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Filestack
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-28 00:00:00.000000000 Z
11
+ date: 2021-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: typhoeus
@@ -45,19 +45,19 @@ dependencies:
45
45
  - !ruby/object:Gem::Version
46
46
  version: 1.11.2
47
47
  - !ruby/object:Gem::Dependency
48
- name: mimemagic
48
+ name: mini_mime
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: 0.3.2
53
+ version: 1.0.2
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: 0.3.2
60
+ version: 1.0.2
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: progress_bar
63
63
  requirement: !ruby/object:Gem::Requirement