cloudinary 1.0.78 → 1.0.79
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.
- checksums.yaml +4 -4
- data/CHANGELOG +10 -0
- data/lib/cloudinary/api.rb +8 -0
- data/lib/cloudinary/carrier_wave/preloaded.rb +2 -2
- data/lib/cloudinary/carrier_wave/process.rb +42 -34
- data/lib/cloudinary/uploader.rb +1 -0
- data/lib/cloudinary/utils.rb +95 -27
- data/lib/cloudinary/version.rb +1 -1
- data/spec/api_spec.rb +111 -94
- data/spec/uploader_spec.rb +33 -33
- data/spec/utils_spec.rb +160 -268
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b29c537f39a98d72f302907e0f50fbb8cafa1a10
|
4
|
+
data.tar.gz: b8a1adc7b8fe8f9bc154f909e44b4ef0a16387fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1fc90e873024c8b1f8f75abf0500684024b3fd5fee87086eecf078645466378bdc3a8fc7965f9c034b3567ff03d8a150f613692b1078110a4aa2721bde21dd79
|
7
|
+
data.tar.gz: 3edd3c23ec7e6053f03a0670e0910dbfddf9260e1ccfe411635c0749db175229eb6b4412b37e0d99591808be159686eea2b6068d407fbb0dccd3aac2a3d4c162
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
= Version 1.0.79 - 2014-12-11
|
2
|
+
* Support folder listing API.
|
3
|
+
* Add support for conditional processing in CarrierWave plugin.
|
4
|
+
* Support tags in upload_large.
|
5
|
+
* Support url_suffix and use_root_path for private_cdn URL building.
|
6
|
+
* Allow using sign_url with type authenticated.
|
7
|
+
* Don't sign version component by default.
|
8
|
+
* Support for new domain sharding syntax and secure domain sharding. Support for secure_cdn_subdomain flag.
|
9
|
+
* Update Cloudinary's jQuery plugin to v1.0.21.
|
10
|
+
|
1
11
|
= Version 1.0.78 - 2014-11-04
|
2
12
|
* Add app_root method that handled Rails.root, which is a String in old Rails versions.
|
3
13
|
* Issue #127 - solve cyclical dependency in case cloudinary was included after Rails was initialized.
|
data/lib/cloudinary/api.rb
CHANGED
@@ -171,6 +171,14 @@ class Cloudinary::Api
|
|
171
171
|
params = Cloudinary::Uploader.build_upload_params(options)
|
172
172
|
call_api(:post, "upload_presets", params.merge(only(options, :name, :unsigned, :disallow_public_id)), options)
|
173
173
|
end
|
174
|
+
|
175
|
+
def self.root_folders(options={})
|
176
|
+
call_api(:get, "folders", {}, options)
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.subfolders(of_folder_path, options={})
|
180
|
+
call_api(:get, "folders/#{of_folder_path}", {}, options)
|
181
|
+
end
|
174
182
|
|
175
183
|
protected
|
176
184
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Copyright Cloudinary
|
2
2
|
# Support for store in CarrierWave files that were preloaded to cloudinary (e.g., by javascript)
|
3
|
-
# Field value must be in the format: "image/upload/v<version
|
3
|
+
# Field value must be in the format: "image/upload/v<version>/<public_id>.<format>#<signature>"
|
4
4
|
# Where signature is the cloduinary API signature on the public_id and version.
|
5
5
|
module Cloudinary::CarrierWave
|
6
6
|
PRELOADED_CLOUDINARY_PATH = Cloudinary::PreloadedFile::PRELOADED_CLOUDINARY_PATH
|
@@ -90,4 +90,4 @@ module Cloudinary::CarrierWave
|
|
90
90
|
return PreloadedCloudinaryFile.new(file) if file.is_a?(String) && file.match(PRELOADED_CLOUDINARY_PATH)
|
91
91
|
nil
|
92
92
|
end
|
93
|
-
end
|
93
|
+
end
|
@@ -3,48 +3,48 @@ module Cloudinary::CarrierWave
|
|
3
3
|
def make_private
|
4
4
|
self.storage_type = :private
|
5
5
|
end
|
6
|
-
|
6
|
+
|
7
7
|
def process_all_versions(*args)
|
8
8
|
@all_versions ||= Class.new(self)
|
9
9
|
@all_versions.process *args
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def eager
|
13
13
|
process :eager => true
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def convert(format)
|
17
17
|
process :convert => format
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def resize_to_limit(width, height)
|
21
21
|
process :resize_to_limit => [width, height]
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def resize_to_fit(width, height)
|
25
25
|
process :resize_to_fit => [width, height]
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
def resize_to_fill(width, height, gravity="Center")
|
29
29
|
process :resize_to_fill => [width, height, gravity]
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def resize_and_pad(width, height, background=:transparent, gravity="Center")
|
33
33
|
process :resize_and_pad => [width, height, background, gravity]
|
34
|
-
end
|
35
|
-
|
34
|
+
end
|
35
|
+
|
36
36
|
def scale(width, height)
|
37
37
|
process :scale => [width, height]
|
38
|
-
end
|
39
|
-
|
38
|
+
end
|
39
|
+
|
40
40
|
def crop(width, height, gravity="Center")
|
41
41
|
process :crop => [width, height, gravity]
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
def cloudinary_transformation(options)
|
45
45
|
process :cloudinary_transformation => options
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
def tags(*tags)
|
49
49
|
process :tags=>tags
|
50
50
|
end
|
@@ -54,35 +54,43 @@ module Cloudinary::CarrierWave
|
|
54
54
|
raise CloudinaryException, "conflicting transformation on #{attr} #{value}!=#{hash[attr]}" if hash[attr] && hash[attr] != value
|
55
55
|
hash[attr] = value
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
def transformation
|
59
59
|
return @transformation if @transformation
|
60
60
|
@transformation = {}
|
61
|
-
self.all_processors.each do
|
62
|
-
|
61
|
+
self.all_processors.each do |name, args, condition|
|
62
|
+
|
63
|
+
if(condition)
|
64
|
+
if condition.respond_to?(:call)
|
65
|
+
next unless condition.call(self, :args => args)
|
66
|
+
else
|
67
|
+
next unless self.send(condition)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
63
71
|
case name
|
64
72
|
when :convert # Do nothing. This is handled by format
|
65
|
-
when :resize_to_limit
|
66
|
-
set_or_yell(@transformation, :width, args[0])
|
73
|
+
when :resize_to_limit
|
74
|
+
set_or_yell(@transformation, :width, args[0])
|
67
75
|
set_or_yell(@transformation, :height, args[1])
|
68
76
|
set_or_yell(@transformation, :crop, :limit)
|
69
|
-
when :resize_to_fit
|
70
|
-
set_or_yell(@transformation, :width, args[0])
|
77
|
+
when :resize_to_fit
|
78
|
+
set_or_yell(@transformation, :width, args[0])
|
71
79
|
set_or_yell(@transformation, :height, args[1])
|
72
80
|
set_or_yell(@transformation, :crop, :fit)
|
73
81
|
when :resize_to_fill
|
74
|
-
set_or_yell(@transformation, :width, args[0])
|
82
|
+
set_or_yell(@transformation, :width, args[0])
|
75
83
|
set_or_yell(@transformation, :height, args[1])
|
76
84
|
set_or_yell(@transformation, :gravity, args[2].to_s.downcase)
|
77
85
|
set_or_yell(@transformation, :crop, :fill)
|
78
86
|
when :resize_and_pad
|
79
|
-
set_or_yell(@transformation, :width, args[0])
|
87
|
+
set_or_yell(@transformation, :width, args[0])
|
80
88
|
set_or_yell(@transformation, :height, args[1])
|
81
89
|
set_or_yell(@transformation, :background, args[2].to_s.downcase)
|
82
90
|
set_or_yell(@transformation, :gravity, args[3].to_s.downcase)
|
83
91
|
set_or_yell(@transformation, :crop, :pad)
|
84
|
-
when :scale
|
85
|
-
set_or_yell(@transformation, :width, args[0])
|
92
|
+
when :scale
|
93
|
+
set_or_yell(@transformation, :width, args[0])
|
86
94
|
set_or_yell(@transformation, :height, args[1])
|
87
95
|
set_or_yell(@transformation, :crop, :scale)
|
88
96
|
when :crop
|
@@ -92,28 +100,28 @@ module Cloudinary::CarrierWave
|
|
92
100
|
set_or_yell(@transformation, :crop, :crop)
|
93
101
|
when :cloudinary_transformation
|
94
102
|
args.each do
|
95
|
-
|attr, value|
|
103
|
+
|attr, value|
|
96
104
|
set_or_yell(@transformation, attr, value)
|
97
105
|
end
|
98
106
|
else
|
99
107
|
if args.blank?
|
100
108
|
send(name).each do
|
101
|
-
|attr, value|
|
109
|
+
|attr, value|
|
102
110
|
set_or_yell(@transformation, attr, value)
|
103
111
|
end
|
104
112
|
end
|
105
113
|
end
|
106
114
|
end
|
107
|
-
@transformation
|
115
|
+
@transformation
|
108
116
|
end
|
109
117
|
|
110
118
|
def all_versions_processors
|
111
119
|
all_versions = self.class.instance_variable_get('@all_versions')
|
112
|
-
|
120
|
+
|
113
121
|
all_versions ? all_versions.processors : []
|
114
122
|
end
|
115
123
|
|
116
|
-
def all_processors
|
124
|
+
def all_processors
|
117
125
|
(self.is_main_uploader? ? [] : all_versions_processors) + self.class.processors
|
118
126
|
end
|
119
127
|
|
@@ -126,22 +134,22 @@ module Cloudinary::CarrierWave
|
|
126
134
|
raise CloudinaryException, "tags cannot be used in versions." if @tags.present? && self.version_name.present?
|
127
135
|
@tags
|
128
136
|
end
|
129
|
-
|
137
|
+
|
130
138
|
def format
|
131
139
|
format_processor = self.all_processors.find{|processor| processor[0] == :convert}
|
132
|
-
if format_processor
|
140
|
+
if format_processor
|
133
141
|
# Explicit format is given
|
134
|
-
format = Array(format_processor[1]).first
|
142
|
+
format = Array(format_processor[1]).first
|
135
143
|
elsif self.transformation.include?(:format)
|
136
144
|
format = self.transformation[:format]
|
137
|
-
elsif self.version_name.present?
|
145
|
+
elsif self.version_name.present?
|
138
146
|
# No local format. The reset should be handled by main uploader
|
139
147
|
uploader = self.model.send(self.mounted_as)
|
140
148
|
format = uploader.format
|
141
149
|
else
|
142
150
|
# Try to auto-detect format
|
143
151
|
format = Cloudinary::PreloadedFile.split_format(original_filename || "").last
|
144
|
-
format ||= "png" # TODO Default format?
|
152
|
+
format ||= "png" # TODO Default format?
|
145
153
|
end
|
146
154
|
format = format.to_s.downcase
|
147
155
|
Cloudinary::FORMAT_ALIASES[format] || format
|
data/lib/cloudinary/uploader.rb
CHANGED
@@ -118,6 +118,7 @@ class Cloudinary::Uploader
|
|
118
118
|
:backup=>options[:backup],
|
119
119
|
:final=>options[:final],
|
120
120
|
:part_number=>options[:part_number],
|
121
|
+
:tags=>options[:tags] && Cloudinary::Utils.build_array(options[:tags]).join(","),
|
121
122
|
:upload_id=>options[:upload_id]
|
122
123
|
}
|
123
124
|
if file.is_a?(Pathname) || !file.respond_to?(:read)
|
data/lib/cloudinary/utils.rb
CHANGED
@@ -124,9 +124,17 @@ class Cloudinary::Utils
|
|
124
124
|
shorten = config_option_consume(options, :shorten)
|
125
125
|
force_remote = options.delete(:force_remote)
|
126
126
|
cdn_subdomain = config_option_consume(options, :cdn_subdomain)
|
127
|
+
secure_cdn_subdomain = config_option_consume(options, :secure_cdn_subdomain)
|
127
128
|
sign_url = config_option_consume(options, :sign_url)
|
128
129
|
secret = config_option_consume(options, :api_secret)
|
129
|
-
|
130
|
+
sign_version = config_option_consume(options, :sign_version) # Deprecated behavior
|
131
|
+
url_suffix = options.delete(:url_suffix)
|
132
|
+
use_root_path = config_option_consume(options, :use_root_path)
|
133
|
+
if !private_cdn
|
134
|
+
raise(CloudinaryException, "URL Suffix only supported in private CDN") unless url_suffix.blank?
|
135
|
+
raise(CloudinaryException, "Root path only supported in private CDN") if use_root_path
|
136
|
+
end
|
137
|
+
|
130
138
|
original_source = source
|
131
139
|
return original_source if source.blank?
|
132
140
|
if defined?(CarrierWave::Uploader::Base) && source.is_a?(CarrierWave::Uploader::Base)
|
@@ -152,47 +160,107 @@ class Cloudinary::Utils
|
|
152
160
|
end
|
153
161
|
end
|
154
162
|
|
155
|
-
type
|
163
|
+
resource_type, type = finalize_resource_type(resource_type, type, url_suffix, use_root_path, shorten)
|
164
|
+
source, source_to_sign = finalize_source(source, format, url_suffix)
|
165
|
+
|
166
|
+
version ||= 1 if source_to_sign.include?("/") and !source_to_sign.match(/^v[0-9]+/) and !source_to_sign.match(/^https?:\//)
|
167
|
+
version &&= "v#{version}"
|
168
|
+
|
169
|
+
transformation = transformation.gsub(%r(([^:])//), '\1/')
|
170
|
+
if sign_url
|
171
|
+
to_sign = [transformation, sign_version && version, source_to_sign].reject(&:blank?).join("/")
|
172
|
+
signature = 's--' + Base64.urlsafe_encode64(Digest::SHA1.digest(to_sign + secret))[0,8] + '--'
|
173
|
+
end
|
174
|
+
|
175
|
+
prefix = unsigned_download_url_prefix(source, cloud_name, private_cdn, cdn_subdomain, secure_cdn_subdomain, cname, secure, secure_distribution)
|
176
|
+
source = [prefix, resource_type, type, signature, transformation, version, source].reject(&:blank?).join("/")
|
177
|
+
end
|
156
178
|
|
179
|
+
def self.finalize_source(source, format, url_suffix)
|
180
|
+
source = source.gsub(%r(([^:])//), '\1/')
|
157
181
|
if source.match(%r(^https?:/)i)
|
158
182
|
source = smart_escape(source)
|
183
|
+
source_to_sign = source
|
159
184
|
else
|
160
185
|
source = smart_escape(URI.decode(source))
|
161
|
-
|
186
|
+
source_to_sign = source
|
187
|
+
unless url_suffix.blank?
|
188
|
+
raise(CloudinaryException, "url_suffix should not include . or /") if url_suffix.match(%r([\./]))
|
189
|
+
source = "#{source}/#{url_suffix}"
|
190
|
+
end
|
191
|
+
if !format.blank?
|
192
|
+
source = "#{source}.#{format}"
|
193
|
+
source_to_sign = "#{source_to_sign}.#{format}"
|
194
|
+
end
|
162
195
|
end
|
196
|
+
[source, source_to_sign]
|
197
|
+
end
|
163
198
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
prefix = "https://#{secure_distribution}"
|
199
|
+
def self.finalize_resource_type(resource_type, type, url_suffix, use_root_path, shorten)
|
200
|
+
type ||= :upload
|
201
|
+
if !url_suffix.blank?
|
202
|
+
if resource_type.to_s == "image" && type.to_s == "upload"
|
203
|
+
resource_type = "images"
|
204
|
+
type = nil
|
205
|
+
elsif resource_type.to_s == "raw" && type.to_s == "upload"
|
206
|
+
resource_type = "files"
|
207
|
+
type = nil
|
174
208
|
else
|
175
|
-
|
176
|
-
|
177
|
-
|
209
|
+
raise(CloudinaryException, "URL Suffix only supported for image/upload and raw/upload")
|
210
|
+
end
|
211
|
+
end
|
212
|
+
if use_root_path
|
213
|
+
if (resource_type.to_s == "image" && type.to_s == "upload") || (resource_type.to_s == "images" && type.blank?)
|
214
|
+
resource_type = nil
|
215
|
+
type = nil
|
216
|
+
else
|
217
|
+
raise(CloudinaryException, "Root path only supported for image/upload")
|
178
218
|
end
|
179
|
-
prefix += "/#{cloud_name}" if shared_domain
|
180
219
|
end
|
181
|
-
|
182
220
|
if shorten && resource_type.to_s == "image" && type.to_s == "upload"
|
183
221
|
resource_type = "iu"
|
184
222
|
type = nil
|
185
223
|
end
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
224
|
+
[resource_type, type]
|
225
|
+
end
|
226
|
+
|
227
|
+
# cdn_subdomain and secure_cdn_subdomain
|
228
|
+
# 1) Customers in shared distribution (e.g. res.cloudinary.com)
|
229
|
+
# if cdn_domain is true uses res-[1-5].cloudinary.com for both http and https. Setting secure_cdn_subdomain to false disables this for https.
|
230
|
+
# 2) Customers with private cdn
|
231
|
+
# if cdn_domain is true uses cloudname-res-[1-5].cloudinary.com for http
|
232
|
+
# if secure_cdn_domain is true uses cloudname-res-[1-5].cloudinary.com for https (please contact support if you require this)
|
233
|
+
# 3) Customers with cname
|
234
|
+
# if cdn_domain is true uses a[1-5].cname for http. For https, uses the same naming scheme as 1 for shared distribution and as 2 for private distribution.
|
235
|
+
def self.unsigned_download_url_prefix(source, cloud_name, private_cdn, cdn_subdomain, secure_cdn_subdomain, cname, secure, secure_distribution)
|
236
|
+
return "/res#{cloud_name}" if cloud_name.start_with?("/") # For development
|
237
|
+
|
238
|
+
shared_domain = !private_cdn
|
239
|
+
|
240
|
+
if secure
|
241
|
+
if secure_distribution.nil? || secure_distribution == Cloudinary::OLD_AKAMAI_SHARED_CDN
|
242
|
+
secure_distribution = private_cdn ? "#{cloud_name}-res.cloudinary.com" : Cloudinary::SHARED_CDN
|
243
|
+
end
|
244
|
+
shared_domain ||= secure_distribution == Cloudinary::SHARED_CDN
|
245
|
+
secure_cdn_subdomain = cdn_subdomain if secure_cdn_subdomain.nil? && shared_domain
|
246
|
+
|
247
|
+
if secure_cdn_subdomain
|
248
|
+
secure_distribution = secure_distribution.gsub('res.cloudinary.com', "res-#{(Zlib::crc32(source) % 5) + 1}.cloudinary.com")
|
249
|
+
end
|
250
|
+
|
251
|
+
prefix = "https://#{secure_distribution}"
|
252
|
+
elsif cname
|
253
|
+
subdomain = cdn_subdomain ? "a#{(Zlib::crc32(source) % 5) + 1}." : ""
|
254
|
+
prefix = "http://#{subdomain}#{cname}"
|
255
|
+
else
|
256
|
+
host = [private_cdn ? "#{cloud_name}-" : "", "res", cdn_subdomain ? "-#{(Zlib::crc32(source) % 5) + 1}" : "", ".cloudinary.com"].join
|
257
|
+
prefix = "http://#{host}"
|
191
258
|
end
|
192
|
-
|
193
|
-
|
259
|
+
prefix += "/#{cloud_name}" if shared_domain
|
260
|
+
|
261
|
+
prefix
|
194
262
|
end
|
195
|
-
|
263
|
+
|
196
264
|
def self.cloudinary_api_url(action = 'upload', options = {})
|
197
265
|
cloudinary = options[:upload_prefix] || Cloudinary.config.upload_prefix || "https://api.cloudinary.com"
|
198
266
|
cloud_name = options[:cloud_name] || Cloudinary.config.cloud_name || raise(CloudinaryException, "Must supply cloud_name")
|
@@ -239,7 +307,7 @@ class Cloudinary::Utils
|
|
239
307
|
end
|
240
308
|
|
241
309
|
def self.cloudinary_url(public_id, options = {})
|
242
|
-
if options[:type].to_s == 'authenticated'
|
310
|
+
if options[:type].to_s == 'authenticated' && !options[:sign_url]
|
243
311
|
result = signed_download_url(public_id, options)
|
244
312
|
else
|
245
313
|
result = unsigned_download_url(public_id, options)
|
data/lib/cloudinary/version.rb
CHANGED
data/spec/api_spec.rb
CHANGED
@@ -21,53 +21,53 @@ describe Cloudinary::Api do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
it "should allow listing resource_types" do
|
24
|
-
@api.resource_types()["resource_types"].
|
24
|
+
expect(@api.resource_types()["resource_types"]).to include("image")
|
25
25
|
end
|
26
26
|
|
27
27
|
it "should allow listing resources" do
|
28
28
|
resource = @api.resources()["resources"].find{|resource| resource["public_id"] == "api_test"}
|
29
|
-
resource.
|
30
|
-
resource["type"].
|
29
|
+
expect(resource).not_to be_blank
|
30
|
+
expect(resource["type"]).to eq("upload")
|
31
31
|
end
|
32
32
|
|
33
33
|
it "should allow listing resources with cursor" do
|
34
34
|
result = @api.resources(:max_results=>1)
|
35
|
-
result["resources"].
|
36
|
-
result["resources"].length.
|
37
|
-
result["next_cursor"].
|
35
|
+
expect(result["resources"]).not_to be_blank
|
36
|
+
expect(result["resources"].length).to eq(1)
|
37
|
+
expect(result["next_cursor"]).not_to be_blank
|
38
38
|
result2 = @api.resources(:max_results=>1, :next_cursor=>result["next_cursor"])
|
39
|
-
result2["resources"].
|
40
|
-
result2["resources"].length.
|
41
|
-
result2["resources"][0]["public_id"].
|
39
|
+
expect(result2["resources"]).not_to be_blank
|
40
|
+
expect(result2["resources"].length).to eq(1)
|
41
|
+
expect(result2["resources"][0]["public_id"]).not_to eq(result["resources"][0]["public_id"] )
|
42
42
|
end
|
43
43
|
|
44
44
|
|
45
45
|
it "should allow listing resources by type" do
|
46
46
|
resource = @api.resources(:type=>"upload", :tags=>true)["resources"].find{|resource| resource["public_id"] == "api_test"}
|
47
|
-
resource.
|
48
|
-
resource["tags"].
|
47
|
+
expect(resource).not_to be_blank
|
48
|
+
expect(resource["tags"]).to eq(["api_test_tag", @timestamp_tag])
|
49
49
|
end
|
50
50
|
|
51
51
|
it "should allow listing resources by prefix" do
|
52
52
|
resources = @api.resources(:type=>"upload", :prefix=>"api_test", :tags => true, :context => true)["resources"]
|
53
|
-
resources.map{|resource| resource["public_id"]}.
|
54
|
-
resources.map{|resource| resource["tags"]}.
|
55
|
-
resources.map{|resource| resource["context"]}.
|
53
|
+
expect(resources.map{|resource| resource["public_id"]}).to include("api_test", "api_test2")
|
54
|
+
expect(resources.map{|resource| resource["tags"]}).to include(["api_test_tag", @timestamp_tag])
|
55
|
+
expect(resources.map{|resource| resource["context"]}).to include({"custom" => {"key" => "value"}})
|
56
56
|
end
|
57
57
|
|
58
58
|
it "should allow listing resources by tag" do
|
59
59
|
resources = @api.resources_by_tag("api_test_tag", :tags => true, :context => true)["resources"]
|
60
|
-
resources.find{|resource| resource["public_id"] == "api_test"}.
|
61
|
-
resources.map{|resource| resource["tags"]}.
|
62
|
-
resources.map{|resource| resource["context"]}.
|
60
|
+
expect(resources.find{|resource| resource["public_id"] == "api_test"}).not_to be_blank
|
61
|
+
expect(resources.map{|resource| resource["tags"]}).to include(["api_test_tag", @timestamp_tag])
|
62
|
+
expect(resources.map{|resource| resource["context"]}).to include({"custom" => {"key" => "value"}})
|
63
63
|
end
|
64
64
|
|
65
65
|
it "should allow listing resources by public ids" do
|
66
66
|
resources = @api.resources_by_ids(["api_test", "api_test2"], :tags => true, :context => true)["resources"]
|
67
|
-
resources.length.
|
68
|
-
resources.find{|resource| resource["public_id"] == "api_test"}.
|
69
|
-
resources.map{|resource| resource["tags"]}.
|
70
|
-
resources.map{|resource| resource["context"]}.
|
67
|
+
expect(resources.length).to eq(2)
|
68
|
+
expect(resources.find{|resource| resource["public_id"] == "api_test"}).not_to be_blank
|
69
|
+
expect(resources.map{|resource| resource["tags"]}).to include(["api_test_tag", @timestamp_tag])
|
70
|
+
expect(resources.map{|resource| resource["context"]}).to include({"custom" => {"key" => "value"}})
|
71
71
|
end
|
72
72
|
|
73
73
|
it "should allow listing resources by start date", :start_at => true do
|
@@ -76,7 +76,7 @@ describe Cloudinary::Api do
|
|
76
76
|
sleep(2)
|
77
77
|
response = Cloudinary::Uploader.upload("spec/logo.png")
|
78
78
|
resources = @api.resources(:type=>"upload", :start_at=>start_at, :direction => "asc")["resources"]
|
79
|
-
resources.map{|resource| resource["public_id"]}
|
79
|
+
expect(resources.map{|resource| resource["public_id"]}) == [response["public_id"]]
|
80
80
|
end
|
81
81
|
|
82
82
|
it "should allow listing resources in both directions" do
|
@@ -84,132 +84,132 @@ describe Cloudinary::Api do
|
|
84
84
|
desc_resources = @api.resources_by_tag(@timestamp_tag, :type=>"upload", :direction => "desc")["resources"]
|
85
85
|
# NOTE: this assumes the full list fits in a page which is the case unless resources with 'api_test' prefix were
|
86
86
|
# uploaded to the account against which this test runs
|
87
|
-
asc_resources.reverse.
|
87
|
+
expect(asc_resources.reverse).to eq(desc_resources)
|
88
88
|
asc_resources_alt = @api.resources_by_tag(@timestamp_tag, :type=>"upload", :direction => 1)["resources"]
|
89
89
|
desc_resources_alt = @api.resources_by_tag(@timestamp_tag, :type=>"upload", :direction => -1)["resources"]
|
90
|
-
asc_resources_alt.reverse.
|
91
|
-
asc_resources.
|
92
|
-
|
90
|
+
expect(asc_resources_alt.reverse).to eq(desc_resources_alt)
|
91
|
+
expect(asc_resources).to eq(asc_resources_alt)
|
92
|
+
expect{@api.resources_by_tag(@timestamp_tag, :type=>"upload", :direction => "anythingelse")["resources"]}.to raise_error(Cloudinary::Api::BadRequest)
|
93
93
|
end
|
94
94
|
|
95
95
|
it "should allow get resource metadata" do
|
96
96
|
resource = @api.resource("api_test")
|
97
|
-
resource.
|
98
|
-
resource["public_id"].
|
99
|
-
resource["bytes"].
|
100
|
-
resource["derived"].length.
|
97
|
+
expect(resource).not_to be_blank
|
98
|
+
expect(resource["public_id"]).to eq("api_test")
|
99
|
+
expect(resource["bytes"]).to eq(3381)
|
100
|
+
expect(resource["derived"].length).to eq(1)
|
101
101
|
end
|
102
102
|
|
103
103
|
it "should allow deleting derived resource" do
|
104
104
|
Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test3", :eager=>[:width=>101,:crop=>:scale])
|
105
105
|
resource = @api.resource("api_test3")
|
106
|
-
resource.
|
107
|
-
resource["derived"].length.
|
106
|
+
expect(resource).not_to be_blank
|
107
|
+
expect(resource["derived"].length).to eq(1)
|
108
108
|
derived_resource_id = resource["derived"][0]["id"]
|
109
109
|
@api.delete_derived_resources(derived_resource_id)
|
110
110
|
resource = @api.resource("api_test3")
|
111
|
-
resource.
|
112
|
-
resource["derived"].length.
|
111
|
+
expect(resource).not_to be_blank
|
112
|
+
expect(resource["derived"].length).to eq(0)
|
113
113
|
end
|
114
114
|
|
115
115
|
it "should allow deleting resources" do
|
116
116
|
Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test3")
|
117
117
|
resource = @api.resource("api_test3")
|
118
|
-
resource.
|
118
|
+
expect(resource).not_to be_blank
|
119
119
|
@api.delete_resources(["apit_test", "api_test2", "api_test3"])
|
120
|
-
|
120
|
+
expect{@api.resource("api_test3")}.to raise_error(Cloudinary::Api::NotFound)
|
121
121
|
end
|
122
122
|
|
123
123
|
it "should allow deleting resources by prefix" do
|
124
124
|
Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test_by_prefix")
|
125
125
|
resource = @api.resource("api_test_by_prefix")
|
126
|
-
resource.
|
126
|
+
expect(resource).not_to be_blank
|
127
127
|
@api.delete_resources_by_prefix("api_test_by")
|
128
|
-
|
128
|
+
expect{@api.resource("api_test_by_prefix")}.to raise_error(Cloudinary::Api::NotFound)
|
129
129
|
end
|
130
130
|
|
131
131
|
it "should allow deleting resources by tags" do
|
132
132
|
Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test4", :tags=>["api_test_tag_for_delete"])
|
133
133
|
resource = @api.resource("api_test4")
|
134
|
-
resource.
|
134
|
+
expect(resource).not_to be_blank
|
135
135
|
@api.delete_resources_by_tag("api_test_tag_for_delete")
|
136
|
-
|
136
|
+
expect{@api.resource("api_test4")}.to raise_error(Cloudinary::Api::NotFound)
|
137
137
|
end
|
138
138
|
|
139
139
|
it "should allow listing tags" do
|
140
140
|
tags = @api.tags()["tags"]
|
141
|
-
tags.
|
141
|
+
expect(tags).to include('api_test_tag')
|
142
142
|
end
|
143
143
|
|
144
144
|
it "should allow listing tag by prefix" do
|
145
145
|
tags = @api.tags(:prefix=>"api_test")["tags"]
|
146
|
-
tags.
|
146
|
+
expect(tags).to include('api_test_tag')
|
147
147
|
tags = @api.tags(:prefix=>"api_test_no_such_tag")["tags"]
|
148
|
-
tags.
|
148
|
+
expect(tags).to be_blank
|
149
149
|
end
|
150
150
|
|
151
151
|
it "should allow listing transformations" do
|
152
152
|
transformation = @api.transformations()["transformations"].find{|transformation| transformation["name"] == "c_scale,w_100"}
|
153
|
-
transformation.
|
154
|
-
transformation["used"].
|
153
|
+
expect(transformation).not_to be_blank
|
154
|
+
expect(transformation["used"]).to eq(true)
|
155
155
|
end
|
156
156
|
|
157
157
|
it "should allow getting transformation metadata" do
|
158
158
|
transformation = @api.transformation("c_scale,w_100")
|
159
|
-
transformation.
|
160
|
-
transformation["info"].
|
159
|
+
expect(transformation).not_to be_blank
|
160
|
+
expect(transformation["info"]).to eq(["crop"=>"scale", "width"=>100] )
|
161
161
|
transformation = @api.transformation("crop"=>"scale", "width"=>100)
|
162
|
-
transformation.
|
163
|
-
transformation["info"].
|
162
|
+
expect(transformation).not_to be_blank
|
163
|
+
expect(transformation["info"]).to eq(["crop"=>"scale", "width"=>100] )
|
164
164
|
end
|
165
165
|
|
166
166
|
it "should allow updating transformation allowed_for_strict" do
|
167
167
|
@api.update_transformation("c_scale,w_100", :allowed_for_strict=>true)
|
168
168
|
transformation = @api.transformation("c_scale,w_100")
|
169
|
-
transformation.
|
170
|
-
transformation["allowed_for_strict"].
|
169
|
+
expect(transformation).not_to be_blank
|
170
|
+
expect(transformation["allowed_for_strict"]).to eq(true)
|
171
171
|
@api.update_transformation("c_scale,w_100", :allowed_for_strict=>false)
|
172
172
|
transformation = @api.transformation("c_scale,w_100")
|
173
|
-
transformation.
|
174
|
-
transformation["allowed_for_strict"].
|
173
|
+
expect(transformation).not_to be_blank
|
174
|
+
expect(transformation["allowed_for_strict"]).to eq(false)
|
175
175
|
end
|
176
176
|
|
177
177
|
it "should allow creating named transformation" do
|
178
178
|
@api.create_transformation("api_test_transformation", "crop"=>"scale", "width"=>102)
|
179
179
|
transformation = @api.transformation("api_test_transformation")
|
180
|
-
transformation.
|
181
|
-
transformation["allowed_for_strict"].
|
182
|
-
transformation["info"].
|
183
|
-
transformation["used"].
|
180
|
+
expect(transformation).not_to be_blank
|
181
|
+
expect(transformation["allowed_for_strict"]).to eq(true)
|
182
|
+
expect(transformation["info"]).to eq(["crop"=>"scale", "width"=>102])
|
183
|
+
expect(transformation["used"]).to eq(false)
|
184
184
|
end
|
185
185
|
|
186
186
|
it "should allow deleting named transformation" do
|
187
187
|
@api.create_transformation("api_test_transformation2", "crop"=>"scale", "width"=>103)
|
188
188
|
@api.transformation("api_test_transformation2")
|
189
189
|
@api.delete_transformation("api_test_transformation2")
|
190
|
-
|
190
|
+
expect{@api.transformation("api_test_transformation2")}.to raise_error(Cloudinary::Api::NotFound)
|
191
191
|
end
|
192
192
|
|
193
193
|
it "should allow unsafe update of named transformation" do
|
194
194
|
@api.create_transformation("api_test_transformation3", "crop"=>"scale", "width"=>102)
|
195
195
|
@api.update_transformation("api_test_transformation3", :unsafe_update=>{"crop"=>"scale", "width"=>103})
|
196
196
|
transformation = @api.transformation("api_test_transformation3")
|
197
|
-
transformation.
|
198
|
-
transformation["info"].
|
199
|
-
transformation["used"].
|
197
|
+
expect(transformation).not_to be_blank
|
198
|
+
expect(transformation["info"]).to eq(["crop"=>"scale", "width"=>103])
|
199
|
+
expect(transformation["used"]).to eq(false)
|
200
200
|
end
|
201
201
|
|
202
202
|
it "should allow deleting implicit transformation" do
|
203
203
|
@api.transformation("c_scale,w_100")
|
204
204
|
@api.delete_transformation("c_scale,w_100")
|
205
|
-
|
205
|
+
expect{@api.transformation("c_scale,w_100")}.to raise_error(Cloudinary::Api::NotFound)
|
206
206
|
end
|
207
207
|
|
208
208
|
it "should allow creating and listing upload_presets", :upload_preset => true do
|
209
209
|
@api.create_upload_preset(:name => "api_test_upload_preset", :folder => "folder")
|
210
210
|
@api.create_upload_preset(:name => "api_test_upload_preset2", :folder => "folder2")
|
211
211
|
@api.create_upload_preset(:name => "api_test_upload_preset3", :folder => "folder3")
|
212
|
-
@api.upload_presets["presets"].first(3).map{|p| p["name"]}.
|
212
|
+
expect(@api.upload_presets["presets"].first(3).map{|p| p["name"]}).to eq(["api_test_upload_preset3", "api_test_upload_preset2", "api_test_upload_preset"])
|
213
213
|
@api.delete_upload_preset("api_test_upload_preset")
|
214
214
|
@api.delete_upload_preset("api_test_upload_preset2")
|
215
215
|
@api.delete_upload_preset("api_test_upload_preset3")
|
@@ -219,12 +219,12 @@ describe Cloudinary::Api do
|
|
219
219
|
result = @api.create_upload_preset(:unsigned => true, :folder => "folder", :width => 100, :crop => :scale, :tags => ["a","b","c"], :context => {:a => "b", :c => "d"})
|
220
220
|
name = result["name"]
|
221
221
|
preset = @api.upload_preset(name)
|
222
|
-
preset["name"].
|
223
|
-
preset["unsigned"].
|
224
|
-
preset["settings"]["folder"].
|
225
|
-
preset["settings"]["transformation"].
|
226
|
-
preset["settings"]["context"].
|
227
|
-
preset["settings"]["tags"].
|
222
|
+
expect(preset["name"]).to eq(name)
|
223
|
+
expect(preset["unsigned"]).to eq(true)
|
224
|
+
expect(preset["settings"]["folder"]).to eq("folder")
|
225
|
+
expect(preset["settings"]["transformation"]).to eq([{"width" => 100, "crop" => "scale"}])
|
226
|
+
expect(preset["settings"]["context"]).to eq({"a" => "b", "c" => "d"})
|
227
|
+
expect(preset["settings"]["tags"]).to eq(["a","b","c"])
|
228
228
|
@api.delete_upload_preset(name)
|
229
229
|
end
|
230
230
|
|
@@ -232,7 +232,7 @@ describe Cloudinary::Api do
|
|
232
232
|
@api.create_upload_preset(:name => "api_test_upload_preset4", :folder => "folder")
|
233
233
|
preset = @api.upload_preset("api_test_upload_preset4")
|
234
234
|
@api.delete_upload_preset("api_test_upload_preset4")
|
235
|
-
|
235
|
+
expect{preset = @api.upload_preset("api_test_upload_preset4")}.to raise_error
|
236
236
|
end
|
237
237
|
|
238
238
|
it "should allow updating upload_presets", :upload_preset => true do
|
@@ -240,9 +240,9 @@ describe Cloudinary::Api do
|
|
240
240
|
preset = @api.upload_preset(name)
|
241
241
|
@api.update_upload_preset(name, preset["settings"].merge(:colors => true, :unsigned => true, :disallow_public_id => true))
|
242
242
|
preset = @api.upload_preset(name)
|
243
|
-
preset["name"].
|
244
|
-
preset["unsigned"].
|
245
|
-
preset["settings"].
|
243
|
+
expect(preset["name"]).to eq(name)
|
244
|
+
expect(preset["unsigned"]).to eq(true)
|
245
|
+
expect(preset["settings"]).to eq({"folder" => "folder", "colors" => true, "disallow_public_id" => true})
|
246
246
|
@api.delete_upload_preset(name)
|
247
247
|
end
|
248
248
|
|
@@ -250,41 +250,41 @@ describe Cloudinary::Api do
|
|
250
250
|
it "should allow deleting all resources", :delete_all=>true do
|
251
251
|
Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test5", :eager=>[:width=>101,:crop=>:scale])
|
252
252
|
resource = @api.resource("api_test5")
|
253
|
-
resource.
|
254
|
-
resource["derived"].length.
|
253
|
+
expect(resource).not_to be_blank
|
254
|
+
expect(resource["derived"].length).to eq(1)
|
255
255
|
@api.delete_all_resources(:keep_original => true)
|
256
256
|
resource = @api.resource("api_test5")
|
257
|
-
resource.
|
258
|
-
resource["derived"].length.
|
257
|
+
expect(resource).not_to be_blank
|
258
|
+
expect(resource["derived"].length).to eq(0)
|
259
259
|
end
|
260
260
|
|
261
261
|
it "should support setting manual moderation status" do
|
262
262
|
result = Cloudinary::Uploader.upload("spec/logo.png", {:moderation => :manual})
|
263
|
-
result["moderation"][0]["status"].
|
264
|
-
result["moderation"][0]["kind"].
|
263
|
+
expect(result["moderation"][0]["status"]).to eq("pending")
|
264
|
+
expect(result["moderation"][0]["kind"]).to eq("manual")
|
265
265
|
api_result = Cloudinary::Api.update(result["public_id"], {:moderation_status => :approved})
|
266
|
-
api_result["moderation"][0]["status"].
|
267
|
-
api_result["moderation"][0]["kind"].
|
266
|
+
expect(api_result["moderation"][0]["status"]).to eq("approved")
|
267
|
+
expect(api_result["moderation"][0]["kind"]).to eq("manual")
|
268
268
|
end
|
269
269
|
|
270
270
|
it "should support requesting raw conversion" do
|
271
271
|
result = Cloudinary::Uploader.upload("spec/docx.docx", :resource_type => :raw)
|
272
|
-
|
272
|
+
expect{Cloudinary::Api.update(result["public_id"], {:resource_type => :raw, :raw_convert => :illegal})}.to raise_error(Cloudinary::Api::BadRequest, /^Illegal value|not a valid/)
|
273
273
|
end
|
274
274
|
|
275
275
|
it "should support requesting categorization" do
|
276
276
|
result = Cloudinary::Uploader.upload("spec/logo.png")
|
277
|
-
|
277
|
+
expect{Cloudinary::Api.update(result["public_id"], {:categorization => :illegal})}.to raise_error(Cloudinary::Api::BadRequest, /^Illegal value/)
|
278
278
|
end
|
279
279
|
|
280
280
|
it "should support requesting detection" do
|
281
281
|
result = Cloudinary::Uploader.upload("spec/logo.png")
|
282
|
-
|
282
|
+
expect{Cloudinary::Api.update(result["public_id"], {:detection => :illegal})}.to raise_error(Cloudinary::Api::BadRequest, /^Illegal value/)
|
283
283
|
end
|
284
284
|
|
285
285
|
it "should support requesting auto_tagging" do
|
286
286
|
result = Cloudinary::Uploader.upload("spec/logo.png")
|
287
|
-
|
287
|
+
expect{Cloudinary::Api.update(result["public_id"], {:auto_tagging => 0.5})}.to raise_error(Cloudinary::Api::BadRequest, /^Must use/)
|
288
288
|
end
|
289
289
|
|
290
290
|
it "should support listing by moderation kind and value" do
|
@@ -294,17 +294,34 @@ describe Cloudinary::Api do
|
|
294
294
|
Cloudinary::Api.update(result1["public_id"], {:moderation_status => :approved})
|
295
295
|
Cloudinary::Api.update(result2["public_id"], {:moderation_status => :rejected})
|
296
296
|
approved = Cloudinary::Api.resources_by_moderation(:manual, :approved, :max_results => 1000)["resources"].map{|r| r["public_id"]}
|
297
|
-
approved.
|
298
|
-
approved.
|
299
|
-
approved.
|
297
|
+
expect(approved).to include(result1["public_id"])
|
298
|
+
expect(approved).not_to include(result2["public_id"])
|
299
|
+
expect(approved).not_to include(result3["public_id"])
|
300
300
|
rejected = Cloudinary::Api.resources_by_moderation(:manual, :rejected, :max_results => 1000)["resources"].map{|r| r["public_id"]}
|
301
|
-
rejected.
|
302
|
-
rejected.
|
303
|
-
rejected.
|
301
|
+
expect(rejected).to include(result2["public_id"])
|
302
|
+
expect(rejected).not_to include(result1["public_id"])
|
303
|
+
expect(rejected).not_to include(result3["public_id"])
|
304
304
|
pending = Cloudinary::Api.resources_by_moderation(:manual, :pending, :max_results => 1000)["resources"].map{|r| r["public_id"]}
|
305
|
-
pending.
|
306
|
-
pending.
|
307
|
-
pending.
|
305
|
+
expect(pending).to include(result3["public_id"])
|
306
|
+
expect(pending).not_to include(result1["public_id"])
|
307
|
+
expect(pending).not_to include(result2["public_id"])
|
308
308
|
end
|
309
309
|
|
310
|
+
it "should support listing folders" do
|
311
|
+
pending("For this test to work, 'Auto-create folders' should be enabled in the Upload Settings, " +
|
312
|
+
"and the account should be empty of folders. " +
|
313
|
+
"Comment out this line if you really want to test it.")
|
314
|
+
Cloudinary::Uploader.upload("spec/logo.png", {:public_id => "test_folder1/item"})
|
315
|
+
Cloudinary::Uploader.upload("spec/logo.png", {:public_id => "test_folder2/item"})
|
316
|
+
Cloudinary::Uploader.upload("spec/logo.png", {:public_id => "test_folder1/test_subfolder1/item"})
|
317
|
+
Cloudinary::Uploader.upload("spec/logo.png", {:public_id => "test_folder1/test_subfolder2/item"})
|
318
|
+
result = Cloudinary::Api.root_folders
|
319
|
+
expect(result["folders"][0]["name"]).to eq("test_folder1")
|
320
|
+
expect(result["folders"][1]["name"]).to eq("test_folder2")
|
321
|
+
result = Cloudinary::Api.subfolders("test_folder1")
|
322
|
+
expect(result["folders"][0]["path"]).to eq("test_folder1/test_subfolder1")
|
323
|
+
expect(result["folders"][1]["path"]).to eq("test_folder1/test_subfolder2")
|
324
|
+
expect{Cloudinary::Api.subfolders("test_folder")}.to raise_error(Cloudinary::Api::NotFound)
|
325
|
+
Cloudinary::Api.delete_resources_by_prefix("test_folder")
|
326
|
+
end
|
310
327
|
end
|