cloudinary 1.26.0 → 1.28.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9bb33366a739a067a66cbcaf66534e48663e726d57f4d4b3657c44ec43f092e7
4
- data.tar.gz: 0dd05a5beabf06fc8d1914c7375fa7a6d37fce532edca0021919e13d9b28d318
3
+ metadata.gz: 97b754051e806086f5dfb98479292bb84bed48968c242d8c55dfa073a8d90e52
4
+ data.tar.gz: 6a6484d454b0b074fa177b68e90bb62a100c90aa3f4b3ab33dad9acd64d7ec4d
5
5
  SHA512:
6
- metadata.gz: 64cb7b27cd9909da6fbbaaf98777d4f05046098a6adb1572998323fd7517bf30c73095844683c83da59808f5945c93e90f1e2c685e06ff156b682fe92e902878
7
- data.tar.gz: f098206a8c9f6c73609ad2415cfc90f8359c678461d0bb22f5164aba57111fe55129eed6e98767c6242387ee6294bf4139b034848a1ff9b792cf1dba9fa3ef77
6
+ metadata.gz: dd822c6ec7144df3f7ab863d834802c486bc31ef5051cafbb9d331279a0f3092906990c211ccf1802515d53d51f64e71fd1bd73acc8a2f368c22bddd90ea3a38
7
+ data.tar.gz: cc2299facddd71188d08c0f934ad8483bc0c9414364699e3f84a20eee489f0883adca4ba838a9534d417e11580ff194761d0d26cc978079c9e3b2f42cceeaa48
data/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ 1.28.0 / 2023-11-06
2
+ ==================
3
+
4
+ New functionality and features
5
+ ------------------------------
6
+
7
+ * Add support for `image_file` parameter in `visual_search` Admin API
8
+ * Add support for `on_success` upload parameter
9
+
10
+ Other Changes
11
+ -------------
12
+
13
+ * Replace `update_all` to `update_column` in CarrierWave storage
14
+
15
+ 1.27.0 / 2023-07-31
16
+ ==================
17
+
18
+ New functionality and features
19
+ ------------------------------
20
+
21
+ * Add support for `visual_search` Admin API
22
+ * Add support for Search URL
23
+ * Add support for `SearchFolders` API
24
+ * Add support for `media_metadata` API parameter
25
+
1
26
  1.26.0 / 2023-06-01
2
27
  ==================
3
28
 
@@ -207,6 +207,20 @@ class Cloudinary::Api
207
207
  call_api(:get, uri, params, options)
208
208
  end
209
209
 
210
+ # Find images based on their visual content.
211
+ #
212
+ # @param [Hash] options The optional parameters.
213
+ #
214
+ # @return [Cloudinary::Api::Response]
215
+ #
216
+ # @raise [Cloudinary::Api::Error]
217
+ def self.visual_search(options = {})
218
+ uri = "resources/visual_search"
219
+ params = only(options, :image_url, :image_asset_id, :text, :image_file)
220
+ params[:image_file] = Cloudinary::Utils.handle_file_param(params[:image_file]) if params.has_key?(:image_file)
221
+ call_api(:post, uri, params, options)
222
+ end
223
+
210
224
  # Returns the details of the specified asset and all its derived assets.
211
225
  #
212
226
  # Note that if you only need details about the original asset,
@@ -1293,6 +1307,7 @@ class Cloudinary::Api
1293
1307
  :faces,
1294
1308
  :quality_analysis,
1295
1309
  :image_metadata,
1310
+ :media_metadata,
1296
1311
  :phash,
1297
1312
  :pages,
1298
1313
  :cinemagraph_analysis,
@@ -82,14 +82,7 @@ class Cloudinary::CarrierWave::Storage < ::CarrierWave::Storage::Abstract
82
82
  end
83
83
 
84
84
  if defined?(ActiveRecord::Base) && uploader.model.is_a?(ActiveRecord::Base)
85
- primary_key = model_class.primary_key.to_sym
86
- if defined?(::ActiveRecord::VERSION::MAJOR) && ::ActiveRecord::VERSION::MAJOR > 2
87
- model_class.where(primary_key=>uploader.model.send(primary_key)).update_all(column=>name)
88
- else
89
- # Removed since active record version 3.0.0
90
- model_class.update_all({column=>name}, {primary_key=>uploader.model.send(primary_key)})
91
- end
92
- uploader.model.send :write_attribute, column, name
85
+ uploader.model.update_column(column, name)
93
86
  elsif defined?(Mongoid::Document) && uploader.model.is_a?(Mongoid::Document)
94
87
  # Mongoid support
95
88
  if Mongoid::VERSION.split(".").first.to_i >= 4
@@ -1,15 +1,23 @@
1
1
  class Cloudinary::Search
2
+ ENDPOINT = 'resources'
3
+
2
4
  SORT_BY = :sort_by
3
5
  AGGREGATE = :aggregate
4
6
  WITH_FIELD = :with_field
5
7
  KEYS_WITH_UNIQUE_VALUES = [SORT_BY, AGGREGATE, WITH_FIELD].freeze
6
8
 
9
+ TTL = 300 # Used for search URLs
10
+
7
11
  def initialize
8
12
  @query_hash = {
9
13
  SORT_BY => {},
10
14
  AGGREGATE => {},
11
15
  WITH_FIELD => {}
12
16
  }
17
+
18
+ @endpoint = self.class::ENDPOINT
19
+
20
+ @ttl = self.class::TTL
13
21
  end
14
22
 
15
23
  ## implicitly generate an instance delegate the method
@@ -69,11 +77,21 @@ class Cloudinary::Search
69
77
  self
70
78
  end
71
79
 
80
+ # Sets the time to live of the search URL.
81
+ #
82
+ # @param [Object] ttl The time to live in seconds.
83
+ #
84
+ # @return [Cloudinary::Search]
85
+ def ttl(ttl)
86
+ @ttl = ttl
87
+ self
88
+ end
89
+
72
90
  # Returns the query as an hash.
73
91
  #
74
92
  # @return [Hash]
75
93
  def to_h
76
- @query_hash.each_with_object({}) do |(key, value), query|
94
+ @query_hash.sort.each_with_object({}) do |(key, value), query|
77
95
  next if value.nil? || ((value.is_a?(Array) || value.is_a?(Hash)) && value.blank?)
78
96
 
79
97
  query[key] = KEYS_WITH_UNIQUE_VALUES.include?(key) ? value.values : value
@@ -82,7 +100,44 @@ class Cloudinary::Search
82
100
 
83
101
  def execute(options = {})
84
102
  options[:content_type] = :json
85
- uri = 'resources/search'
103
+ uri = "#{@endpoint}/search"
86
104
  Cloudinary::Api.call_api(:post, uri, to_h, options)
87
105
  end
106
+
107
+ # Creates a signed Search URL that can be used on the client side.
108
+ #
109
+ # @param [Integer] ttl The time to live in seconds.
110
+ # @param [String] next_cursor Starting position.
111
+ # @param [Hash] options Additional url delivery options.
112
+ #
113
+ # @return [String] The resulting Search URL
114
+ def to_url(ttl = nil, next_cursor = nil, options = {})
115
+ api_secret = options[:api_secret] || Cloudinary.config.api_secret || raise(CloudinaryException, "Must supply api_secret")
116
+
117
+ ttl = ttl || @ttl
118
+ query = self.to_h
119
+
120
+ _next_cursor = query.delete(:next_cursor)
121
+ next_cursor = _next_cursor if next_cursor.nil?
122
+
123
+ b64query = Base64.urlsafe_encode64(JSON.generate(query))
124
+
125
+ prefix = Cloudinary::Utils.build_distribution_domain(options)
126
+
127
+ signature = Cloudinary::Utils.hash("#{ttl}#{b64query}#{api_secret}", :sha256, :hexdigest)
128
+
129
+ next_cursor = "/#{next_cursor}" if !next_cursor.nil? && !next_cursor.empty?
130
+
131
+ "#{prefix}/search/#{signature}/#{ttl}/#{b64query}#{next_cursor}"
132
+ end
133
+
134
+ # Sets the API endpoint.
135
+ #
136
+ # @param [String] endpoint the endpoint to set.
137
+ #
138
+ # @return [Cloudinary::Search]
139
+ def endpoint(endpoint)
140
+ @endpoint = endpoint
141
+ self
142
+ end
88
143
  end
@@ -0,0 +1,5 @@
1
+ # The Cloudinary API folders search method allows you fine control on filtering and retrieving information on all the
2
+ # folders in your cloud with the help of query expressions in a Lucene-like query language.
3
+ class Cloudinary::SearchFolders < Cloudinary::Search
4
+ ENDPOINT = 'folders'
5
+ end
@@ -40,6 +40,7 @@ class Cloudinary::Uploader
40
40
  :eager_notification_url => options[:eager_notification_url],
41
41
  :exif => Cloudinary::Utils.as_safe_bool(options[:exif]),
42
42
  :eval => options[:eval],
43
+ :on_success => options[:on_success],
43
44
  :face_coordinates => Cloudinary::Utils.encode_double_array(options[:face_coordinates]),
44
45
  :faces => Cloudinary::Utils.as_safe_bool(options[:faces]),
45
46
  :folder => options[:folder],
@@ -47,6 +48,7 @@ class Cloudinary::Uploader
47
48
  :filename_override => options[:filename_override],
48
49
  :headers => build_custom_headers(options[:headers]),
49
50
  :image_metadata => Cloudinary::Utils.as_safe_bool(options[:image_metadata]),
51
+ :media_metadata => Cloudinary::Utils.as_safe_bool(options[:media_metadata]),
50
52
  :invalidate => Cloudinary::Utils.as_safe_bool(options[:invalidate]),
51
53
  :moderation => options[:moderation],
52
54
  :notification_url => options[:notification_url],
@@ -62,6 +64,7 @@ class Cloudinary::Uploader
62
64
  :responsive_breakpoints => Cloudinary::Utils.generate_responsive_breakpoints_string(options[:responsive_breakpoints]),
63
65
  :return_delete_token => Cloudinary::Utils.as_safe_bool(options[:return_delete_token]),
64
66
  :similarity_search => options[:similarity_search],
67
+ :visual_search => Cloudinary::Utils.as_safe_bool(options[:visual_search]),
65
68
  :tags => options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(","),
66
69
  :timestamp => (options[:timestamp] || Time.now.to_i),
67
70
  :transformation => Cloudinary::Utils.generate_transformation_string(options.clone),
@@ -85,16 +88,7 @@ class Cloudinary::Uploader
85
88
  def self.upload(file, options={})
86
89
  call_api("upload", options) do
87
90
  params = build_upload_params(options)
88
- if file.is_a?(Pathname)
89
- params[:file] = File.open(file, "rb")
90
- elsif file.is_a?(StringIO)
91
- file.rewind
92
- params[:file] = Cloudinary::Blob.new(file.read, options)
93
- elsif file.respond_to?(:read) || Cloudinary::Utils.is_remote?(file)
94
- params[:file] = file
95
- else
96
- params[:file] = File.open(file, "rb")
97
- end
91
+ params[:file] = Cloudinary::Utils.handle_file_param(file, options)
98
92
  [params, [:file]]
99
93
  end
100
94
  end
@@ -151,11 +145,7 @@ class Cloudinary::Uploader
151
145
  options[:resource_type] ||= :raw
152
146
  call_api("upload", options) do
153
147
  params = build_upload_params(options)
154
- if file.is_a?(Pathname) || !file.respond_to?(:read)
155
- params[:file] = File.open(file, "rb")
156
- else
157
- params[:file] = file
158
- end
148
+ params[:file] = Cloudinary::Utils.handle_file_param(file, options)
159
149
  [params, [:file]]
160
150
  end
161
151
  end
@@ -524,18 +524,10 @@ class Cloudinary::Utils
524
524
  version = options.delete(:version)
525
525
  force_version = config_option_consume(options, :force_version, true)
526
526
  format = options.delete(:format)
527
- cloud_name = config_option_consume(options, :cloud_name) || raise(CloudinaryException, "Must supply cloud_name in tag or in configuration")
528
527
 
529
- secure = options.delete(:secure)
530
- ssl_detected = options.delete(:ssl_detected)
531
- secure = ssl_detected || Cloudinary.config.secure if secure.nil?
532
- private_cdn = config_option_consume(options, :private_cdn)
533
- secure_distribution = config_option_consume(options, :secure_distribution)
534
- cname = config_option_consume(options, :cname)
535
528
  shorten = config_option_consume(options, :shorten)
536
529
  force_remote = options.delete(:force_remote)
537
- cdn_subdomain = config_option_consume(options, :cdn_subdomain)
538
- secure_cdn_subdomain = config_option_consume(options, :secure_cdn_subdomain)
530
+
539
531
  sign_url = config_option_consume(options, :sign_url)
540
532
  secret = config_option_consume(options, :api_secret)
541
533
  sign_version = config_option_consume(options, :sign_version) # Deprecated behavior
@@ -558,7 +550,7 @@ class Cloudinary::Utils
558
550
  type = type.to_s unless type.nil?
559
551
  resource_type ||= "image"
560
552
  source = source.to_s
561
- if !force_remote
553
+ unless force_remote
562
554
  static_support = Cloudinary.config.static_file_support || Cloudinary.config.static_image_support
563
555
  return original_source if !static_support && type == "asset"
564
556
  return original_source if (type.nil? || type == "asset") && source.match(%r(^https?:/)i)
@@ -594,7 +586,9 @@ class Cloudinary::Utils
594
586
  signature = "s--#{signature[0, long_url_signature ? LONG_URL_SIGNATURE_LENGTH : SHORT_URL_SIGNATURE_LENGTH ]}--"
595
587
  end
596
588
 
597
- prefix = unsigned_download_url_prefix(source, cloud_name, private_cdn, cdn_subdomain, secure_cdn_subdomain, cname, secure, secure_distribution)
589
+ options[:source] = source
590
+ prefix = build_distribution_domain(options)
591
+
598
592
  source = [prefix, resource_type, type, signature, transformation, version, source].reject(&:blank?).join("/")
599
593
  if sign_url && auth_token && !auth_token.empty?
600
594
  auth_token[:url] = URI.parse(source).path
@@ -707,6 +701,22 @@ class Cloudinary::Utils
707
701
  prefix
708
702
  end
709
703
 
704
+ def self.build_distribution_domain(options = {})
705
+ cloud_name = config_option_consume(options, :cloud_name) || raise(CloudinaryException, "Must supply cloud_name in tag or in configuration")
706
+
707
+ source = options.delete(:source)
708
+ secure = options.delete(:secure)
709
+ ssl_detected = options.delete(:ssl_detected)
710
+ secure = ssl_detected || Cloudinary.config.secure if secure.nil?
711
+ private_cdn = config_option_consume(options, :private_cdn)
712
+ secure_distribution = config_option_consume(options, :secure_distribution)
713
+ cname = config_option_consume(options, :cname)
714
+ cdn_subdomain = config_option_consume(options, :cdn_subdomain)
715
+ secure_cdn_subdomain = config_option_consume(options, :secure_cdn_subdomain)
716
+
717
+ unsigned_download_url_prefix(source, cloud_name, private_cdn, cdn_subdomain, secure_cdn_subdomain, cname, secure, secure_distribution)
718
+ end
719
+
710
720
  # Creates a base URL for the cloudinary api
711
721
  #
712
722
  # @param [Object] path Resource name
@@ -1285,6 +1295,27 @@ class Cloudinary::Utils
1285
1295
  }
1286
1296
  end
1287
1297
 
1298
+ # Handles file parameter.
1299
+ #
1300
+ # @param [Pathname, StringIO, File, String, int, _ToPath] file
1301
+ # @return [StringIO, File] A File object.
1302
+ #
1303
+ # @private
1304
+ def self.handle_file_param(file, options = {})
1305
+ if file.is_a?(Pathname)
1306
+ return File.open(file, "rb")
1307
+ elsif file.is_a?(Cloudinary::Blob)
1308
+ return file
1309
+ elsif file.is_a?(StringIO)
1310
+ file.rewind
1311
+ return Cloudinary::Blob.new(file.read, options)
1312
+ elsif file.respond_to?(:read) || Cloudinary::Utils.is_remote?(file)
1313
+ return file
1314
+ end
1315
+
1316
+ File.open(file, "rb")
1317
+ end
1318
+
1288
1319
  # The returned url should allow downloading the backedup asset based on the version and asset id
1289
1320
  #
1290
1321
  # asset and version id are returned with resource(<PUBLIC_ID1>, { versions: true })
@@ -1373,6 +1404,4 @@ class Cloudinary::Utils
1373
1404
  algorithm = ALGORITHM_SIGNATURE[signature_algorithm] || raise("Unsupported algorithm '#{signature_algorithm}'")
1374
1405
  algorithm.public_send(hash_method, input)
1375
1406
  end
1376
-
1377
- private_class_method :hash
1378
1407
  end
@@ -1,4 +1,4 @@
1
1
  # Copyright Cloudinary
2
2
  module Cloudinary
3
- VERSION = "1.26.0"
3
+ VERSION = "1.28.0"
4
4
  end
data/lib/cloudinary.rb CHANGED
@@ -28,6 +28,7 @@ module Cloudinary
28
28
  autoload :Static, "cloudinary/static"
29
29
  autoload :CarrierWave, "cloudinary/carrier_wave"
30
30
  autoload :Search, "cloudinary/search"
31
+ autoload :SearchFolders, "cloudinary/search_folders"
31
32
 
32
33
  CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"
33
34
  AKAMAI_SHARED_CDN = "res.cloudinary.com"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudinary
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.26.0
4
+ version: 1.28.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nadav Soferman
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-06-01 00:00:00.000000000 Z
13
+ date: 2023-11-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: aws_cf_signer
@@ -235,6 +235,7 @@ files:
235
235
  - lib/cloudinary/railtie.rb
236
236
  - lib/cloudinary/responsive.rb
237
237
  - lib/cloudinary/search.rb
238
+ - lib/cloudinary/search_folders.rb
238
239
  - lib/cloudinary/static.rb
239
240
  - lib/cloudinary/uploader.rb
240
241
  - lib/cloudinary/utils.rb