cloudinary 1.29.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,29 +6,29 @@ class Cloudinary::Migrator
6
6
  attr_reader :db
7
7
  attr_reader :work, :results, :mutex
8
8
  attr_reader :extra_options
9
-
10
-
9
+
10
+
11
11
  @@init = false
12
12
  def self.init
13
13
  return if @@init
14
14
  @@init = true
15
15
 
16
- begin
16
+ begin
17
17
  require 'sqlite3'
18
18
  rescue LoadError
19
- raise "Please add sqlite3 to your Gemfile"
19
+ raise "Please add sqlite3 to your Gemfile"
20
20
  end
21
21
  require 'tempfile'
22
22
  end
23
-
23
+
24
24
  def json_decode(str)
25
25
  Cloudinary::Utils.json_decode(str)
26
26
  end
27
-
27
+
28
28
  def initialize(options={})
29
29
  self.class.init
30
-
31
- options[:db_file] = "tmp/migration#{$$}.db" if options[:private_database] && !options[:db_file]
30
+
31
+ options[:db_file] = "tmp/migration#{$$}.db" if options[:private_database] && !options[:db_file]
32
32
  @dbfile = options[:db_file] || "tmp/migration.db"
33
33
  FileUtils.mkdir_p(File.dirname(@dbfile))
34
34
  @db = SQLite3::Database.new @dbfile, :results_as_hash=>true
@@ -37,7 +37,6 @@ class Cloudinary::Migrator
37
37
  @debug = options[:debug] || false
38
38
  @ignore_duplicates = options[:ignore_duplicates]
39
39
  @threads = [options[:threads] || 10, 100].min
40
- @threads = 1 if RUBY_VERSION < "1.9"
41
40
  @extra_options = {:api_key=>options[:api_key], :api_secret=>options[:api_secret]}
42
41
  @delete_after_done = options[:delete_after_done] || options[:private_database]
43
42
  @max_processing = @threads * 10
@@ -77,7 +76,7 @@ class Cloudinary::Migrator
77
76
  @db.execute("delete from queue")
78
77
  end
79
78
  end
80
-
79
+
81
80
  def register_retrieve(&block)
82
81
  @retrieve = block
83
82
  end
@@ -85,26 +84,26 @@ class Cloudinary::Migrator
85
84
  def register_complete(&block)
86
85
  @complete = block
87
86
  end
88
-
89
- def process(options={})
87
+
88
+ def process(options={})
90
89
  raise CloudinaryException, "url not given and no retrieve callback given" if options[:url].nil? && self.retrieve.nil?
91
90
  raise CloudinaryException, "id not given and retrieve or complete callback given" if options[:id].nil? && (!self.retrieve.nil? || !self.complete.nil?)
92
91
 
93
92
  debug("Process: #{options.inspect}")
94
93
  start
95
- process_results
94
+ process_results
96
95
  wait_for_queue
97
96
  options = options.dup
98
97
  id = options.delete(:id)
99
98
  url = options.delete(:url)
100
99
  public_id = options.delete(:public_id)
101
100
  row = {
102
- "internal_id"=>id,
103
- "url"=>url,
104
- "public_id"=>public_id,
101
+ "internal_id"=>id,
102
+ "url"=>url,
103
+ "public_id"=>public_id,
105
104
  "metadata"=>options.to_json,
106
- "status"=>"processing"
107
- }
105
+ "status"=>"processing"
106
+ }
108
107
  begin
109
108
  insert_row(row)
110
109
  add_to_work_queue(row)
@@ -112,7 +111,7 @@ class Cloudinary::Migrator
112
111
  raise if !@ignore_duplicates
113
112
  end
114
113
  end
115
-
114
+
116
115
  def done
117
116
  start
118
117
  process_all_pending
@@ -122,37 +121,37 @@ class Cloudinary::Migrator
122
121
  @db.close
123
122
  File.delete(@dbfile) if @delete_after_done
124
123
  end
125
-
124
+
126
125
  def max_given_id
127
126
  db.get_first_value("select max(internal_id) from queue").to_i
128
127
  end
129
-
128
+
130
129
  def close_if_needed(file)
131
130
  if file.nil?
132
131
  # Do nothing.
133
- elsif file.respond_to?(:close!)
134
- file.close!
132
+ elsif file.respond_to?(:close!)
133
+ file.close!
135
134
  elsif file.respond_to?(:close)
136
135
  file.close
137
136
  end
138
137
  rescue
139
138
  # Ignore errors in closing files
140
- end
139
+ end
141
140
 
142
- def temporary_file(data, filename)
143
- file = RUBY_VERSION == "1.8.7" ? Tempfile.new('cloudinary') : Tempfile.new('cloudinary', :encoding => 'ascii-8bit')
141
+ def temporary_file(data, filename)
142
+ file = Tempfile.new('cloudinary', :encoding => 'ascii-8bit')
144
143
  file.unlink
145
144
  file.write(data)
146
145
  file.rewind
147
- # Tempfile return path == nil after unlink, which break rest-client
146
+ # Tempfile return path == nil after unlink, which break rest-client
148
147
  class << file
149
148
  attr_accessor :original_filename
150
149
  def content_type
151
- "application/octet-stream"
150
+ "application/octet-stream"
152
151
  end
153
152
  end
154
153
  file.original_filename = filename
155
- file
154
+ file
156
155
  end
157
156
 
158
157
  private
@@ -160,21 +159,21 @@ class Cloudinary::Migrator
160
159
  def update_all(values)
161
160
  @db.execute("update queue set #{values.keys.map{|key| "#{key}=?"}.join(",")}", *values.values)
162
161
  end
163
-
164
- def update_row(row, values)
162
+
163
+ def update_row(row, values)
165
164
  values.merge!("updated_at"=>Time.now.to_i)
166
165
  query = ["update queue set #{values.keys.map{|key| "#{key}=?"}.join(",")} where id=?"] + values.values + [row["id"]]
167
166
  @db.execute(*query)
168
167
  values.each{|key, value| row[key.to_s] = value}
169
- row
168
+ row
170
169
  end
171
-
170
+
172
171
  def insert_row(values)
173
172
  values.merge!("updated_at"=>Time.now.to_i)
174
173
  @db.execute("insert into queue (#{values.keys.join(",")}) values (#{values.keys.map{"?"}.join(",")})", *values.values)
175
174
  values["id"] = @db.last_insert_row_id
176
175
  end
177
-
176
+
178
177
  def refill_queue(last_id)
179
178
  @db.execute("select * from queue where status in ('error', 'processing') and id > ? limit ?", last_id, 10000) do
180
179
  |row|
@@ -183,25 +182,25 @@ class Cloudinary::Migrator
183
182
  add_to_work_queue(row)
184
183
  end
185
184
  last_id
186
- end
185
+ end
187
186
 
188
187
  def process_results
189
188
  while self.results.length > 0
190
189
  row = self.results.pop
191
- result = json_decode(row["result"])
190
+ result = json_decode(row["result"])
192
191
  debug("Done ID=#{row['internal_id']}, result=#{result.inspect}")
193
- complete.call(row["internal_id"], result) if complete
194
- if result["error"]
192
+ complete.call(row["internal_id"], result) if complete
193
+ if result["error"]
195
194
  status = case result["error"]["http_code"]
196
195
  when 400, 404 then "fatal" # Problematic request. Not a server problem.
197
196
  else "error"
198
197
  end
199
198
  else
200
199
  status = "completed"
201
- end
200
+ end
202
201
  updates = {:status=>status, :result=>row["result"]}
203
202
  updates["public_id"] = result["public_id"] if result["public_id"] && !row["public_id"]
204
- begin
203
+ begin
205
204
  update_row(row, updates)
206
205
  rescue SQLite3::ConstraintException
207
206
  updates = {:status=>"error", :result=>{:error=>{:message=>"public_id already exists"}}.to_json}
@@ -219,16 +218,16 @@ class Cloudinary::Migrator
219
218
  raise if retry_count > tries
220
219
  sleep rand * 3
221
220
  retry
222
- end
221
+ end
223
222
  end
224
-
223
+
225
224
  def start
226
225
  return if @started
227
226
  @started = true
228
227
  @terminate = false
229
-
228
+
230
229
  self.work.clear
231
-
230
+
232
231
  main = self
233
232
  Thread.abort_on_exception = true
234
233
  1.upto(@threads) do
@@ -251,8 +250,8 @@ class Cloudinary::Migrator
251
250
  elsif defined?(::CarrierWave) && defined?(Cloudinary::CarrierWave) && data.is_a?(Cloudinary::CarrierWave)
252
251
  cw = true
253
252
  begin
254
- data.model.save!
255
- rescue Cloudinary::CarrierWave::UploadError
253
+ data.model.save!
254
+ rescue Cloudinary::CarrierWave::UploadError
256
255
  # upload errors will be handled by the result values.
257
256
  end
258
257
  result = data.metadata
@@ -264,22 +263,22 @@ class Cloudinary::Migrator
264
263
  elsif data.match(/^https?:/)
265
264
  url = data
266
265
  else
267
- file = main.temporary_file(data, row["public_id"] || "cloudinaryfile")
266
+ file = main.temporary_file(data, row["public_id"] || "cloudinaryfile")
268
267
  end
269
268
  end
270
-
269
+
271
270
  if url || file
272
271
  options = main.extra_options.merge(:public_id=>row["public_id"])
273
272
  json_decode(row["metadata"]).each do
274
273
  |key, value|
275
274
  options[key.to_sym] = value
276
275
  end
277
-
276
+
278
277
  result = Cloudinary::Uploader.upload(url || file, options.merge(:return_error=>true)) || ({:error=>{:message=>"Received nil from uploader!"}})
279
278
  elsif cw
280
279
  result ||= {"status" => "saved"}
281
280
  else
282
- result = {"error" => {"message" => "Empty data and url", "http_code"=>404}}
281
+ result = {"error" => {"message" => "Empty data and url", "http_code"=>404}}
283
282
  end
284
283
  main.results << {"id"=>row["id"], "internal_id"=>row["internal_id"], "result"=>result.to_json}
285
284
  rescue => e
@@ -289,14 +288,14 @@ class Cloudinary::Migrator
289
288
  ensure
290
289
  main.mutex.synchronize{main.in_process -= 1}
291
290
  main.close_if_needed(file)
292
- end
291
+ end
293
292
  end
294
293
  end
295
- end
296
-
294
+ end
295
+
297
296
  retry_previous_queue # Retry all work from previous iteration before we start processing this one.
298
297
  end
299
-
298
+
300
299
  def debug(message)
301
300
  if @debug
302
301
  mutex.synchronize{
@@ -310,60 +309,60 @@ class Cloudinary::Migrator
310
309
  begin
311
310
  prev_last_id, last_id = last_id, refill_queue(last_id)
312
311
  end while last_id > prev_last_id
313
- process_results
312
+ process_results
314
313
  end
315
-
314
+
316
315
  def process_all_pending
317
316
  # Waiting for work to finish. While we are at it, process results.
318
- while self.in_process > 0
317
+ while self.in_process > 0
319
318
  process_results
320
319
  sleep 0.1
321
320
  end
322
321
  # Make sure we processed all the results
323
322
  process_results
324
323
  end
325
-
324
+
326
325
  def add_to_work_queue(row)
327
326
  self.work << row
328
327
  mutex.synchronize{self.in_process += 1}
329
328
  end
330
-
329
+
331
330
  def wait_for_queue
332
331
  # Waiting f
333
332
  while self.work.length > @max_processing
334
- process_results
333
+ process_results
335
334
  sleep 0.1
336
- end
335
+ end
337
336
  end
338
337
 
339
- def self.sample
338
+ def self.sample
340
339
  migrator = Cloudinary::Migrator.new(
341
- :retrieve=>proc{|id| Post.find(id).data},
340
+ :retrieve=>proc{|id| Post.find(id).data},
342
341
  :complete=>proc{|id, result| a}
343
342
  )
344
-
343
+
345
344
  Post.find_each(:conditions=>["id > ?", migrator.max_given_id], :select=>"id") do
346
345
  |post|
347
346
  migrator.process(:id=>post.id, :public_id=>"post_#{post.id}")
348
347
  end
349
348
  migrator.done
350
- end
351
-
352
- def self.test
349
+ end
350
+
351
+ def self.test
353
352
  posts = {}
354
- done = {}
353
+ done = {}
355
354
  migrator = Cloudinary::Migrator.new(
356
- :retrieve=>proc{|id| posts[id]},
355
+ :retrieve=>proc{|id| posts[id]},
357
356
  :complete=>proc{|id, result| $stderr.print "done #{id} #{result}\n"; done[id] = result}
358
357
  )
359
358
  start = migrator.max_given_id + 1
360
359
  (start..1000).each{|i| posts[i] = "hello#{i}"}
361
-
360
+
362
361
  posts.each do
363
362
  |id, data|
364
363
  migrator.process(:id=>id, :public_id=>"post_#{id}")
365
364
  end
366
365
  migrator.done
367
366
  pp [done.length, start]
368
- end
367
+ end
369
368
  end
@@ -1,83 +1,53 @@
1
1
  # Copyright Cloudinary
2
- require 'rest_client'
2
+ require 'faraday'
3
3
  require 'json'
4
4
  require 'cloudinary/cache'
5
5
 
6
6
  class Cloudinary::Uploader
7
-
8
- REMOTE_URL_REGEX = Cloudinary::Utils::REMOTE_URL_REGEX
9
- # @deprecated use {Cloudinary::Utils.build_eager} instead
10
- def self.build_eager(eager)
11
- Cloudinary::Utils.build_eager(eager)
12
- end
13
-
14
- # @private
15
- def self.build_upload_params(options)
7
+ @adapter = nil
8
+ def self.build_upload_params(options, as_bool = false)
16
9
  #symbolize keys
17
10
  options = options.clone
18
11
  options.keys.each { |key| options[key.to_sym] = options.delete(key) if key.is_a?(String) }
19
12
 
20
13
  params = {
21
- :access_control => Cloudinary::Utils.json_array_param(options[:access_control]),
22
- :access_mode => options[:access_mode],
23
- :allowed_formats => Cloudinary::Utils.build_array(options[:allowed_formats]).join(","),
24
- :asset_folder => options[:asset_folder],
25
- :async => Cloudinary::Utils.as_safe_bool(options[:async]),
26
- :auto_tagging => options[:auto_tagging] && options[:auto_tagging].to_f,
27
- :background_removal => options[:background_removal],
28
- :backup => Cloudinary::Utils.as_safe_bool(options[:backup]),
29
- :callback => options[:callback],
30
- :categorization => options[:categorization],
31
- :cinemagraph_analysis => Cloudinary::Utils.as_safe_bool(options[:cinemagraph_analysis]),
32
- :colors => Cloudinary::Utils.as_safe_bool(options[:colors]),
33
- :context => Cloudinary::Utils.encode_context(options[:context]),
34
- :custom_coordinates => Cloudinary::Utils.encode_double_array(options[:custom_coordinates]),
35
- :detection => options[:detection],
36
- :discard_original_filename => Cloudinary::Utils.as_safe_bool(options[:discard_original_filename]),
37
- :display_name => options[:display_name],
38
- :eager => Cloudinary::Utils.build_eager(options[:eager]),
39
- :eager_async => Cloudinary::Utils.as_safe_bool(options[:eager_async]),
40
- :eager_notification_url => options[:eager_notification_url],
41
- :exif => Cloudinary::Utils.as_safe_bool(options[:exif]),
42
- :eval => options[:eval],
43
- :on_success => options[:on_success],
44
- :face_coordinates => Cloudinary::Utils.encode_double_array(options[:face_coordinates]),
45
- :faces => Cloudinary::Utils.as_safe_bool(options[:faces]),
46
- :folder => options[:folder],
47
- :format => options[:format],
48
- :filename_override => options[:filename_override],
49
- :headers => build_custom_headers(options[:headers]),
50
- :image_metadata => Cloudinary::Utils.as_safe_bool(options[:image_metadata]),
51
- :media_metadata => Cloudinary::Utils.as_safe_bool(options[:media_metadata]),
52
- :invalidate => Cloudinary::Utils.as_safe_bool(options[:invalidate]),
53
- :moderation => options[:moderation],
54
- :notification_url => options[:notification_url],
55
- :ocr => options[:ocr],
56
- :overwrite => Cloudinary::Utils.as_safe_bool(options[:overwrite]),
57
- :phash => Cloudinary::Utils.as_safe_bool(options[:phash]),
58
- :proxy => options[:proxy],
59
- :public_id => options[:public_id],
60
- :public_id_prefix => options[:public_id_prefix],
61
- :quality_analysis => Cloudinary::Utils.as_safe_bool(options[:quality_analysis]),
62
- :quality_override => options[:quality_override],
63
- :raw_convert => options[:raw_convert],
64
- :responsive_breakpoints => Cloudinary::Utils.generate_responsive_breakpoints_string(options[:responsive_breakpoints]),
65
- :return_delete_token => Cloudinary::Utils.as_safe_bool(options[:return_delete_token]),
66
- :similarity_search => options[:similarity_search],
67
- :visual_search => Cloudinary::Utils.as_safe_bool(options[:visual_search]),
68
- :tags => options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(","),
69
- :timestamp => (options[:timestamp] || Time.now.to_i),
70
- :transformation => Cloudinary::Utils.generate_transformation_string(options.clone),
71
- :type => options[:type],
72
- :unique_filename => Cloudinary::Utils.as_safe_bool(options[:unique_filename]),
73
- :upload_preset => options[:upload_preset],
74
- :use_filename => Cloudinary::Utils.as_safe_bool(options[:use_filename]),
75
- :use_filename_as_display_name => Cloudinary::Utils.as_safe_bool(options[:use_filename_as_display_name]),
76
- :use_asset_folder_as_public_id_prefix => Cloudinary::Utils.as_safe_bool(options[:use_asset_folder_as_public_id_prefix]),
77
- :unique_display_name => Cloudinary::Utils.as_safe_bool(options[:unique_display_name]),
78
- :accessibility_analysis => Cloudinary::Utils.as_safe_bool(options[:accessibility_analysis]),
79
- :metadata => Cloudinary::Utils.encode_context(options[:metadata])
14
+ :access_control => Cloudinary::Utils.json_array_param(options[:access_control]),
15
+ :allowed_formats => Cloudinary::Utils.build_array(options[:allowed_formats]).join(","),
16
+ :auto_tagging => options[:auto_tagging] && options[:auto_tagging].to_f,
17
+ :context => Cloudinary::Utils.encode_context(options[:context]),
18
+ :custom_coordinates => Cloudinary::Utils.encode_double_array(options[:custom_coordinates]),
19
+ :eager => Cloudinary::Utils.build_eager(options[:eager]),
20
+ :face_coordinates => Cloudinary::Utils.encode_double_array(options[:face_coordinates]),
21
+ :headers => build_custom_headers(options[:headers]),
22
+ :responsive_breakpoints => Cloudinary::Utils.generate_responsive_breakpoints_string(options[:responsive_breakpoints]),
23
+ :tags => options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(","),
24
+ :timestamp => (options[:timestamp] || Time.now.to_i),
25
+ :transformation => Cloudinary::Utils.generate_transformation_string(options.clone),
26
+ :metadata => Cloudinary::Utils.encode_context(options[:metadata])
80
27
  }
28
+
29
+ bool_params = [
30
+ :async, :backup, :cinemagraph_analysis, :colors, :discard_original_filename, :eager_async, :exif, :faces,
31
+ :image_metadata, :media_metadata, :invalidate, :overwrite, :phash, :quality_analysis, :return_delete_token,
32
+ :visual_search, :unique_filename, :use_filename, :use_filename_as_display_name,
33
+ :use_asset_folder_as_public_id_prefix, :unique_display_name, :accessibility_analysis
34
+ ]
35
+
36
+ string_params = [
37
+ :access_mode, :asset_folder, :background_removal, :callback, :categorization, :detection, :display_name,
38
+ :eager_notification_url, :eval, :on_success, :folder, :format, :filename_override, :moderation, :notification_url,
39
+ :ocr, :proxy, :public_id, :public_id_prefix, :quality_override, :raw_convert, :similarity_search, :type,
40
+ :upload_preset
41
+ ]
42
+
43
+ bool_params.each do |b|
44
+ params[b] = as_bool ? Cloudinary::Utils.as_bool(options[b]): Cloudinary::Utils.as_safe_bool(options[b])
45
+ end
46
+
47
+ string_params.each do |s|
48
+ params[s] = options[s]
49
+ end
50
+
81
51
  params
82
52
  end
83
53
 
@@ -394,29 +364,39 @@ class Cloudinary::Uploader
394
364
  params[:signature] = Cloudinary::Utils.api_sign_request(params.reject { |k, v| non_signable.include?(k) }, api_secret, signature_algorithm)
395
365
  params[:api_key] = api_key
396
366
  end
397
- proxy = options[:api_proxy] || Cloudinary.config.api_proxy
398
- timeout = options.fetch(:timeout) { Cloudinary.config.to_h.fetch(:timeout, 60) }
399
-
400
- result = nil
401
-
402
- api_url = Cloudinary::Utils.cloudinary_api_url(action, options)
403
- RestClient::Request.execute(:method => :post, :url => api_url, :payload => params.reject { |k, v| v.nil? || v=="" }, :timeout => timeout, :headers => headers, :proxy => proxy) do
404
- |response, request, tmpresult|
405
- raise CloudinaryException, "Server returned unexpected status code - #{response.code} - #{response.body}" unless [200, 400, 401, 403, 404, 500].include?(response.code)
406
- begin
407
- result = Cloudinary::Utils.json_decode(response.body)
408
- rescue => e
409
- # Error is parsing json
410
- raise CloudinaryException, "Error parsing server response (#{response.code}) - #{response.body}. Got - #{e}"
411
- end
412
- if result["error"]
413
- if return_error
414
- result["error"]["http_code"] = response.code
415
- else
416
- raise CloudinaryException, result["error"]["message"]
417
- end
367
+
368
+ api_url = Cloudinary::Utils.cloudinary_api_url(action, options)
369
+ api_proxy = options[:api_proxy] || Cloudinary.config.api_proxy
370
+ timeout = options.fetch(:timeout) { Cloudinary.config.to_h.fetch(:timeout, 60) }
371
+
372
+ conn = Faraday.new(url: api_url) do |faraday|
373
+ faraday.proxy = api_proxy if api_proxy
374
+ faraday.request :multipart, **options
375
+ faraday.request :url_encoded
376
+ faraday.adapter @adapter || Faraday.default_adapter
377
+ end
378
+
379
+ response = conn.send(:post) do |req|
380
+ req.headers = headers
381
+ req.body = params.reject { |_, v| v.nil? || v=="" }
382
+ req.options.timeout = timeout if timeout
383
+ end
384
+
385
+ raise CloudinaryException, "Server returned unexpected status code - #{response.status} - #{response.body}" unless [200, 400, 401, 403, 404, 500].include?(response.status)
386
+ begin
387
+ result = Cloudinary::Utils.json_decode(response.body)
388
+ rescue => e
389
+ # Error is parsing json
390
+ raise CloudinaryException, "Error parsing server response (#{response.status}) - #{response.body}. Got - #{e}"
391
+ end
392
+ if result["error"]
393
+ if return_error
394
+ result["error"]["http_code"] = response.status
395
+ else
396
+ raise CloudinaryException, result["error"]["message"]
418
397
  end
419
398
  end
399
+
420
400
  if use_cache && !result.nil?
421
401
  cache_results(result)
422
402
  end