cloudinary 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -0
- data/cloudinary.gemspec +1 -0
- data/lib/cloudinary.rb +21 -2
- data/lib/cloudinary/api.rb +126 -94
- data/lib/cloudinary/helper.rb +17 -4
- data/lib/cloudinary/uploader.rb +190 -167
- data/lib/cloudinary/utils.rb +138 -14
- data/lib/cloudinary/version.rb +1 -1
- data/spec/api_spec.rb +168 -115
- data/spec/archive_spec.rb +102 -0
- data/spec/cloudinary_spec.rb +17 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/uploader_spec.rb +90 -44
- data/spec/utils_spec.rb +183 -176
- data/spec/video_tag_spec.rb +7 -2
- metadata +20 -2
data/lib/cloudinary/utils.rb
CHANGED
@@ -3,6 +3,8 @@ require 'digest/sha1'
|
|
3
3
|
require 'zlib'
|
4
4
|
require 'uri'
|
5
5
|
require 'aws_cf_signer'
|
6
|
+
require 'json'
|
7
|
+
require 'cgi'
|
6
8
|
|
7
9
|
class Cloudinary::Utils
|
8
10
|
# @deprecated Use Cloudinary::SHARED_CDN
|
@@ -10,9 +12,9 @@ class Cloudinary::Utils
|
|
10
12
|
DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION = {:width => :auto, :crop => :limit}
|
11
13
|
|
12
14
|
# Warning: options are being destructively updated!
|
13
|
-
def self.generate_transformation_string(options={})
|
15
|
+
def self.generate_transformation_string(options={}, allow_implicit_crop_mode = false)
|
14
16
|
if options.is_a?(Array)
|
15
|
-
return options.map{|base_transformation| generate_transformation_string(base_transformation.clone)}.join("/")
|
17
|
+
return options.map{|base_transformation| generate_transformation_string(base_transformation.clone, allow_implicit_crop_mode)}.join("/")
|
16
18
|
end
|
17
19
|
|
18
20
|
symbolize_keys!(options)
|
@@ -32,7 +34,7 @@ class Cloudinary::Utils
|
|
32
34
|
options.delete(:width) if width && (width.to_f < 1 || no_html_sizes || width == "auto" || responsive_width)
|
33
35
|
options.delete(:height) if height && (height.to_f < 1 || no_html_sizes || responsive_width)
|
34
36
|
|
35
|
-
width=height=nil if crop.nil? && !has_layer && width != "auto"
|
37
|
+
width=height=nil if crop.nil? && !has_layer && width != "auto" && !allow_implicit_crop_mode
|
36
38
|
|
37
39
|
background = options.delete(:background)
|
38
40
|
background = background.sub(/^#/, 'rgb:') if background
|
@@ -44,7 +46,7 @@ class Cloudinary::Utils
|
|
44
46
|
if base_transformations.any?{|base_transformation| base_transformation.is_a?(Hash)}
|
45
47
|
base_transformations = base_transformations.map do
|
46
48
|
|base_transformation|
|
47
|
-
base_transformation.is_a?(Hash) ? generate_transformation_string(base_transformation.clone) : generate_transformation_string(:transformation=>base_transformation)
|
49
|
+
base_transformation.is_a?(Hash) ? generate_transformation_string(base_transformation.clone, allow_implicit_crop_mode) : generate_transformation_string({:transformation=>base_transformation}, allow_implicit_crop_mode)
|
48
50
|
end
|
49
51
|
else
|
50
52
|
named_transformation = base_transformations.join(".")
|
@@ -87,6 +89,7 @@ class Cloudinary::Utils
|
|
87
89
|
:w => width
|
88
90
|
}
|
89
91
|
{
|
92
|
+
:ar => :aspect_ratio,
|
90
93
|
:ac => :audio_codec,
|
91
94
|
:br => :bit_rate,
|
92
95
|
:cs => :color_space,
|
@@ -125,7 +128,7 @@ class Cloudinary::Utils
|
|
125
128
|
transformations = base_transformations << transformation
|
126
129
|
if responsive_width
|
127
130
|
responsive_width_transformation = Cloudinary.config.responsive_width_transformation || DEFAULT_RESPONSIVE_WIDTH_TRANSFORMATION
|
128
|
-
transformations << generate_transformation_string(responsive_width_transformation.clone)
|
131
|
+
transformations << generate_transformation_string(responsive_width_transformation.clone, allow_implicit_crop_mode)
|
129
132
|
end
|
130
133
|
|
131
134
|
if width.to_s == "auto" || responsive_width
|
@@ -207,6 +210,8 @@ class Cloudinary::Utils
|
|
207
210
|
end
|
208
211
|
letter_spacing = layer[:letter_spacing]
|
209
212
|
keywords.push("letter_spacing_#{letter_spacing}") unless letter_spacing.blank?
|
213
|
+
line_spacing = layer[:line_spacing]
|
214
|
+
keywords.push("line_spacing_#{line_spacing}") unless line_spacing.blank?
|
210
215
|
if !font_size.blank? || !font_family.blank? || !keywords.empty?
|
211
216
|
raise(CloudinaryException, "Must supply font_family for text in overlay/underlay") if font_family.blank?
|
212
217
|
raise(CloudinaryException, "Must supply font_size for text in overlay/underlay") if font_size.blank?
|
@@ -225,6 +230,23 @@ class Cloudinary::Utils
|
|
225
230
|
Digest::SHA1.hexdigest("#{to_sign}#{api_secret}")
|
226
231
|
end
|
227
232
|
|
233
|
+
def self.generate_responsive_breakpoints_string(breakpoints)
|
234
|
+
return nil if breakpoints.nil?
|
235
|
+
breakpoints = build_array(breakpoints)
|
236
|
+
|
237
|
+
breakpoints.map do |breakpoint_settings|
|
238
|
+
unless breakpoint_settings.nil?
|
239
|
+
breakpoint_settings = breakpoint_settings.clone
|
240
|
+
transformation = breakpoint_settings.delete(:transformation) || breakpoint_settings.delete("transformation")
|
241
|
+
if transformation
|
242
|
+
breakpoint_settings[:transformation] = Cloudinary::Utils.generate_transformation_string(transformation.clone, true)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
breakpoint_settings
|
246
|
+
|
247
|
+
end.to_json
|
248
|
+
end
|
249
|
+
|
228
250
|
# Warning: options are being destructively updated!
|
229
251
|
def self.unsigned_download_url(source, options = {})
|
230
252
|
|
@@ -354,14 +376,21 @@ class Cloudinary::Utils
|
|
354
376
|
[resource_type, type]
|
355
377
|
end
|
356
378
|
|
379
|
+
# Creates the URL prefix for the cloudinary resource URL
|
380
|
+
#
|
357
381
|
# cdn_subdomain and secure_cdn_subdomain
|
358
|
-
# 1
|
359
|
-
#
|
360
|
-
#
|
361
|
-
#
|
362
|
-
#
|
363
|
-
#
|
364
|
-
#
|
382
|
+
# 1. Customers in shared distribution (e.g. res.cloudinary.com)
|
383
|
+
#
|
384
|
+
# 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.
|
385
|
+
# 2. Customers with private cdn
|
386
|
+
#
|
387
|
+
# if cdn_domain is true uses cloudname-res-[1-5 ].cloudinary.com for http
|
388
|
+
#
|
389
|
+
# if secure_cdn_domain is true uses cloudname-res-[1-5 ].cloudinary.com for https (please contact support if you require this)
|
390
|
+
# 3. Customers with cname
|
391
|
+
#
|
392
|
+
# 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.
|
393
|
+
# @private
|
365
394
|
def self.unsigned_download_url_prefix(source, cloud_name, private_cdn, cdn_subdomain, secure_cdn_subdomain, cname, secure, secure_distribution)
|
366
395
|
return "/res#{cloud_name}" if cloud_name.start_with?("/") # For development
|
367
396
|
|
@@ -417,12 +446,53 @@ class Cloudinary::Utils
|
|
417
446
|
:expires_at=>options[:expires_at] && options[:expires_at].to_i
|
418
447
|
}, options)
|
419
448
|
|
420
|
-
return Cloudinary::Utils.cloudinary_api_url("download", options) + "?" + cloudinary_params
|
449
|
+
return Cloudinary::Utils.cloudinary_api_url("download", options) + "?" + hash_query_params(cloudinary_params)
|
421
450
|
end
|
422
451
|
|
452
|
+
# Utility method that uses the deprecated ZIP download API.
|
453
|
+
# @deprecated Replaced by {download_zip_url} that uses the more advanced and robust archive generation and download API
|
423
454
|
def self.zip_download_url(tag, options = {})
|
455
|
+
warn "zip_download_url is deprecated. Please use download_zip_url instead."
|
424
456
|
cloudinary_params = sign_request({:timestamp=>Time.now.to_i, :tag=>tag, :transformation=>generate_transformation_string(options)}, options)
|
425
|
-
return Cloudinary::Utils.cloudinary_api_url("download_tag.zip", options) + "?" + cloudinary_params
|
457
|
+
return Cloudinary::Utils.cloudinary_api_url("download_tag.zip", options) + "?" + hash_query_params(cloudinary_params)
|
458
|
+
end
|
459
|
+
|
460
|
+
# Returns a URL that when invokes creates an archive and returns it.
|
461
|
+
# @param options [Hash]
|
462
|
+
# @option options [String|Symbol] :resource_type The resource type of files to include in the archive. Must be one of :image | :video | :raw
|
463
|
+
# @option options [String|Symbol] :type (:upload) The specific file type of resources: :upload|:private|:authenticated
|
464
|
+
# @option options [String|Symbol|Array] :tags (nil) list of tags to include in the archive
|
465
|
+
# @option options [String|Array<String>] :public_ids (nil) list of public_ids to include in the archive
|
466
|
+
# @option options [String|Array<String>] :prefixes (nil) Optional list of prefixes of public IDs (e.g., folders).
|
467
|
+
# @option options [String|Array<String>] :transformations Optional list of transformations.
|
468
|
+
# The derived images of the given transformations are included in the archive. Using the string representation of
|
469
|
+
# multiple chained transformations as we use for the 'eager' upload parameter.
|
470
|
+
# @option options [String|Symbol] :mode (:create) return the generated archive file or to store it as a raw resource and
|
471
|
+
# return a JSON with URLs for accessing the archive. Possible values: :download, :create
|
472
|
+
# @option options [String|Symbol] :target_format (:zip)
|
473
|
+
# @option options [String] :target_public_id Optional public ID of the generated raw resource.
|
474
|
+
# Relevant only for the create mode. If not specified, random public ID is generated.
|
475
|
+
# @option options [boolean] :flatten_folders (false) If true, flatten public IDs with folders to be in the root of the archive.
|
476
|
+
# Add numeric counter to the file name in case of a name conflict.
|
477
|
+
# @option options [boolean] :flatten_transformations (false) If true, and multiple transformations are given,
|
478
|
+
# flatten the folder structure of derived images and store the transformation details on the file name instead.
|
479
|
+
# @option options [boolean] :use_original_filename Use the original file name of included images (if available) instead of the public ID.
|
480
|
+
# @option options [boolean] :async (false) If true, return immediately and perform the archive creation in the background.
|
481
|
+
# Relevant only for the create mode.
|
482
|
+
# @option options [String] :notification_url Optional URL to send an HTTP post request (webhook) when the archive creation is completed.
|
483
|
+
# @option options [String|Array<String] :target_tags Optional array. Allows assigning one or more tag to the generated archive file (for later housekeeping via the admin API).
|
484
|
+
# @option options [String] :keep_derived (false) keep the derived images used for generating the archive
|
485
|
+
# @return [String] archive url
|
486
|
+
def self.download_archive_url(options = {})
|
487
|
+
cloudinary_params = sign_request(Cloudinary::Utils.archive_params(options.merge(:mode => "download")), options)
|
488
|
+
return Cloudinary::Utils.cloudinary_api_url("generate_archive", options) + "?" + hash_query_params(cloudinary_params)
|
489
|
+
end
|
490
|
+
|
491
|
+
|
492
|
+
# Returns a URL that when invokes creates an zip archive and returns it.
|
493
|
+
# @see download_archive_url
|
494
|
+
def self.download_zip_url(options = {})
|
495
|
+
download_archive_url(options.merge(:target_format => "zip"))
|
426
496
|
end
|
427
497
|
|
428
498
|
def self.signed_download_url(public_id, options = {})
|
@@ -591,8 +661,62 @@ class Cloudinary::Utils
|
|
591
661
|
end
|
592
662
|
end
|
593
663
|
|
664
|
+
# Returns a Hash of parameters used to create an archive
|
665
|
+
# @param [Hash] options
|
666
|
+
# @private
|
667
|
+
def self.archive_params(options = {})
|
668
|
+
options = Cloudinary::Utils.symbolize_keys options
|
669
|
+
{
|
670
|
+
:timestamp=>(options[:timestamp] || Time.now.to_i),
|
671
|
+
:type=>options[:type],
|
672
|
+
:mode => options[:mode],
|
673
|
+
:target_format => options[:target_format],
|
674
|
+
:target_public_id=> options[:target_public_id],
|
675
|
+
:flatten_folders=>Cloudinary::Utils.as_safe_bool(options[:flatten_folders]),
|
676
|
+
:flatten_transformations=>Cloudinary::Utils.as_safe_bool(options[:flatten_transformations]),
|
677
|
+
:use_original_filename=>Cloudinary::Utils.as_safe_bool(options[:use_original_filename]),
|
678
|
+
:async=>Cloudinary::Utils.as_safe_bool(options[:async]),
|
679
|
+
:notification_url=>options[:notification_url],
|
680
|
+
:target_tags=>options[:target_tags] && Cloudinary::Utils.build_array(options[:target_tags]),
|
681
|
+
:keep_derived=>Cloudinary::Utils.as_safe_bool(options[:keep_derived]),
|
682
|
+
:tags=>options[:tags] && Cloudinary::Utils.build_array(options[:tags]),
|
683
|
+
:public_ids=>options[:public_ids] && Cloudinary::Utils.build_array(options[:public_ids]),
|
684
|
+
:prefixes=>options[:prefixes] && Cloudinary::Utils.build_array(options[:prefixes]),
|
685
|
+
:transformations => build_eager(options[:transformations])
|
686
|
+
}
|
687
|
+
end
|
688
|
+
|
689
|
+
# @private
|
690
|
+
def self.build_eager(eager)
|
691
|
+
return nil if eager.nil?
|
692
|
+
Cloudinary::Utils.build_array(eager).map do
|
693
|
+
|transformation, format|
|
694
|
+
transformation = transformation.clone
|
695
|
+
format = transformation.delete(:format) || format
|
696
|
+
[Cloudinary::Utils.generate_transformation_string(transformation, true), format].compact.join("/")
|
697
|
+
end.join("|")
|
698
|
+
end
|
699
|
+
|
594
700
|
private
|
595
701
|
|
702
|
+
def self.hash_query_params(hash)
|
703
|
+
if hash.respond_to?("to_query")
|
704
|
+
hash.to_query
|
705
|
+
else
|
706
|
+
flat_hash_to_query_params(hash)
|
707
|
+
end
|
708
|
+
end
|
709
|
+
|
710
|
+
def self.flat_hash_to_query_params(hash)
|
711
|
+
hash.collect do |key, value|
|
712
|
+
if value.is_a?(Array)
|
713
|
+
value.map{|v| "#{CGI.escape(key.to_s)}[]=#{CGI.escape(v.to_s)}"}.join("&")
|
714
|
+
else
|
715
|
+
"#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"
|
716
|
+
end
|
717
|
+
end.compact.sort!.join('&')
|
718
|
+
end
|
719
|
+
|
596
720
|
def self.number_pattern
|
597
721
|
"([0-9]*)\\.([0-9]+)|([0-9]+)"
|
598
722
|
end
|
data/lib/cloudinary/version.rb
CHANGED
data/spec/api_spec.rb
CHANGED
@@ -3,29 +3,25 @@ require 'cloudinary'
|
|
3
3
|
|
4
4
|
describe Cloudinary::Api do
|
5
5
|
break puts("Please setup environment for api test to run") if Cloudinary.config.api_secret.blank?
|
6
|
+
include_context "cleanup"
|
6
7
|
|
8
|
+
prefix = "api_test_#{Time.now.to_i}"
|
9
|
+
test_id_1 = "#{prefix}_1"
|
10
|
+
test_id_2 = "#{prefix}_2"
|
7
11
|
before(:all) do
|
8
12
|
@timestamp_tag = "api_test_tag_#{Time.now.to_i}"
|
9
13
|
@api = Cloudinary::Api
|
10
|
-
Cloudinary::Uploader.
|
11
|
-
Cloudinary::Uploader.
|
12
|
-
Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test", :tags=> ["api_test_tag", @timestamp_tag], :context => "key=value", :eager=>[:width=>100,:crop=>:scale])
|
13
|
-
Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test2", :tags=> ["api_test_tag", @timestamp_tag], :context => "key=value", :eager=>[:width=>100,:crop=>:scale])
|
14
|
-
@api.delete_transformation("api_test_transformation") rescue nil
|
15
|
-
@api.delete_transformation("api_test_transformation2") rescue nil
|
16
|
-
@api.delete_transformation("api_test_transformation3") rescue nil
|
17
|
-
@api.delete_upload_preset("api_test_upload_preset") rescue nil
|
18
|
-
@api.delete_upload_preset("api_test_upload_preset2") rescue nil
|
19
|
-
@api.delete_upload_preset("api_test_upload_preset3") rescue nil
|
20
|
-
@api.delete_upload_preset("api_test_upload_preset4") rescue nil
|
14
|
+
Cloudinary::Uploader.upload("spec/logo.png", :public_id => test_id_1, :tags => [TEST_TAG, @timestamp_tag], :context => "key=value", :eager =>[:width =>100, :crop =>:scale])
|
15
|
+
Cloudinary::Uploader.upload("spec/logo.png", :public_id => test_id_2, :tags => [TEST_TAG, @timestamp_tag], :context => "key=value", :eager =>[:width =>100, :crop =>:scale])
|
21
16
|
end
|
22
|
-
|
17
|
+
|
23
18
|
it "should allow listing resource_types" do
|
24
19
|
expect(@api.resource_types()["resource_types"]).to include("image")
|
25
20
|
end
|
26
21
|
|
27
22
|
it "should allow listing resources" do
|
28
|
-
resource = @api.resources()["resources"].find{|resource| resource["public_id"] ==
|
23
|
+
resource = @api.resources()["resources"].find{|resource| resource["public_id"] == test_id_1
|
24
|
+
}
|
29
25
|
expect(resource).not_to be_blank
|
30
26
|
expect(resource["type"]).to eq("upload")
|
31
27
|
end
|
@@ -43,30 +39,33 @@ describe Cloudinary::Api do
|
|
43
39
|
|
44
40
|
|
45
41
|
it "should allow listing resources by type" do
|
46
|
-
resource = @api.resources(:type=>"upload", :tags=>true)["resources"].find{|resource| resource["public_id"] ==
|
42
|
+
resource = @api.resources(:type=>"upload", :tags=>true)["resources"].find{|resource| resource["public_id"] == test_id_1
|
43
|
+
}
|
47
44
|
expect(resource).not_to be_blank
|
48
|
-
expect(resource["tags"]).to
|
45
|
+
expect(resource["tags"]).to match_array([TEST_TAG, @timestamp_tag])
|
49
46
|
end
|
50
47
|
|
51
48
|
it "should allow listing resources by prefix" do
|
52
|
-
resources = @api.resources(:type=>"upload", :prefix=>
|
53
|
-
expect(resources.map{|resource| resource["public_id"]}).to include(
|
54
|
-
expect(resources.map{|resource| resource["tags"]}).to include(
|
49
|
+
resources = @api.resources(:type =>"upload", :prefix => prefix, :tags => true, :context => true)["resources"]
|
50
|
+
expect(resources.map{|resource| resource["public_id"]}).to include(test_id_1, test_id_2)
|
51
|
+
expect(resources.map{|resource| resource["tags"]}.flatten).to include(TEST_TAG, @timestamp_tag)
|
55
52
|
expect(resources.map{|resource| resource["context"]}).to include({"custom" => {"key" => "value"}})
|
56
53
|
end
|
57
54
|
|
58
55
|
it "should allow listing resources by tag" do
|
59
|
-
resources = @api.resources_by_tag(
|
60
|
-
expect(resources.find{|resource| resource["public_id"] ==
|
61
|
-
|
56
|
+
resources = @api.resources_by_tag(TEST_TAG, :tags => true, :context => true)["resources"]
|
57
|
+
expect(resources.find{|resource| resource["public_id"] == test_id_1
|
58
|
+
}).not_to be_blank
|
59
|
+
expect(resources.map{|resource| resource["tags"]}.flatten).to include(TEST_TAG, @timestamp_tag)
|
62
60
|
expect(resources.map{|resource| resource["context"]}).to include({"custom" => {"key" => "value"}})
|
63
61
|
end
|
64
62
|
|
65
63
|
it "should allow listing resources by public ids" do
|
66
|
-
resources = @api.resources_by_ids([
|
64
|
+
resources = @api.resources_by_ids([test_id_1, test_id_2], :tags => true, :context => true)["resources"]
|
67
65
|
expect(resources.length).to eq(2)
|
68
|
-
expect(resources.find{|resource| resource["public_id"] ==
|
69
|
-
|
66
|
+
expect(resources.find{|resource| resource["public_id"] == test_id_1
|
67
|
+
}).not_to be_blank
|
68
|
+
expect(resources.map{|resource| resource["tags"]}.flatten).to include(TEST_TAG, @timestamp_tag)
|
70
69
|
expect(resources.map{|resource| resource["context"]}).to include({"custom" => {"key" => "value"}})
|
71
70
|
end
|
72
71
|
|
@@ -93,9 +92,9 @@ describe Cloudinary::Api do
|
|
93
92
|
end
|
94
93
|
|
95
94
|
it "should allow get resource metadata" do
|
96
|
-
resource = @api.resource(
|
95
|
+
resource = @api.resource(test_id_1)
|
97
96
|
expect(resource).not_to be_blank
|
98
|
-
expect(resource["public_id"]).to eq(
|
97
|
+
expect(resource["public_id"]).to eq(test_id_1)
|
99
98
|
expect(resource["bytes"]).to eq(3381)
|
100
99
|
expect(resource["derived"].length).to eq(1)
|
101
100
|
end
|
@@ -116,7 +115,7 @@ describe Cloudinary::Api do
|
|
116
115
|
Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test3")
|
117
116
|
resource = @api.resource("api_test3")
|
118
117
|
expect(resource).not_to be_blank
|
119
|
-
@api.delete_resources(["apit_test",
|
118
|
+
@api.delete_resources(["apit_test", test_id_2, "api_test3"])
|
120
119
|
expect{@api.resource("api_test3")}.to raise_error(Cloudinary::Api::NotFound)
|
121
120
|
end
|
122
121
|
|
@@ -129,7 +128,7 @@ describe Cloudinary::Api do
|
|
129
128
|
end
|
130
129
|
|
131
130
|
it "should allow deleting resources by tags" do
|
132
|
-
Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test4", :tags=>["api_test_tag_for_delete"])
|
131
|
+
Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test4", :tags=>[TEST_TAG, "api_test_tag_for_delete"])
|
133
132
|
resource = @api.resource("api_test4")
|
134
133
|
expect(resource).not_to be_blank
|
135
134
|
@api.delete_resources_by_tag("api_test_tag_for_delete")
|
@@ -138,86 +137,91 @@ describe Cloudinary::Api do
|
|
138
137
|
|
139
138
|
it "should allow listing tags" do
|
140
139
|
tags = @api.tags()["tags"]
|
141
|
-
expect(tags).to include(
|
140
|
+
expect(tags).to include(TEST_TAG)
|
142
141
|
end
|
143
142
|
|
144
143
|
it "should allow listing tag by prefix" do
|
145
|
-
tags = @api.tags(:prefix=>"
|
146
|
-
expect(tags).to include(
|
144
|
+
tags = @api.tags(:prefix=> "api_test_tag")["tags"]
|
145
|
+
expect(tags).to include(@timestamp_tag)
|
147
146
|
tags = @api.tags(:prefix=>"api_test_no_such_tag")["tags"]
|
148
147
|
expect(tags).to be_blank
|
149
148
|
end
|
150
149
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
expect(transformation["used"]).to eq(true)
|
155
|
-
end
|
156
|
-
|
157
|
-
it "should allow getting transformation metadata" do
|
158
|
-
transformation = @api.transformation("c_scale,w_100")
|
159
|
-
expect(transformation).not_to be_blank
|
160
|
-
expect(transformation["info"]).to eq(["crop"=>"scale", "width"=>100] )
|
161
|
-
transformation = @api.transformation("crop"=>"scale", "width"=>100)
|
162
|
-
expect(transformation).not_to be_blank
|
163
|
-
expect(transformation["info"]).to eq(["crop"=>"scale", "width"=>100] )
|
164
|
-
end
|
165
|
-
|
166
|
-
it "should allow updating transformation allowed_for_strict" do
|
167
|
-
@api.update_transformation("c_scale,w_100", :allowed_for_strict=>true)
|
168
|
-
transformation = @api.transformation("c_scale,w_100")
|
169
|
-
expect(transformation).not_to be_blank
|
170
|
-
expect(transformation["allowed_for_strict"]).to eq(true)
|
171
|
-
@api.update_transformation("c_scale,w_100", :allowed_for_strict=>false)
|
172
|
-
transformation = @api.transformation("c_scale,w_100")
|
173
|
-
expect(transformation).not_to be_blank
|
174
|
-
expect(transformation["allowed_for_strict"]).to eq(false)
|
175
|
-
end
|
176
|
-
describe "named transformations" do
|
177
|
-
it "should allow creating named transformation" do
|
178
|
-
@api.create_transformation("api_test_transformation", "crop"=>"scale", "width"=>102)
|
179
|
-
transformation = @api.transformation("api_test_transformation")
|
150
|
+
describe 'transformations' do
|
151
|
+
it "should allow listing transformations" do
|
152
|
+
transformation = @api.transformations()["transformations"].find { |transformation| transformation["name"] == "c_scale,w_100" }
|
180
153
|
expect(transformation).not_to be_blank
|
181
|
-
expect(transformation["
|
182
|
-
expect(transformation["info"]).to eq(["crop"=>"scale", "width"=>102])
|
183
|
-
expect(transformation["used"]).to eq(false)
|
154
|
+
expect(transformation["used"]).to eq(true)
|
184
155
|
end
|
185
156
|
|
186
|
-
it "should allow
|
187
|
-
@api.
|
188
|
-
|
189
|
-
|
190
|
-
|
157
|
+
it "should allow getting transformation metadata" do
|
158
|
+
transformation = @api.transformation("c_scale,w_100")
|
159
|
+
expect(transformation).not_to be_blank
|
160
|
+
expect(transformation["info"]).to eq(["crop" => "scale", "width" => 100])
|
161
|
+
transformation = @api.transformation("crop" => "scale", "width" => 100)
|
162
|
+
expect(transformation).not_to be_blank
|
163
|
+
expect(transformation["info"]).to eq(["crop" => "scale", "width" => 100])
|
191
164
|
end
|
192
165
|
|
193
|
-
it "should allow
|
194
|
-
@api.
|
195
|
-
@api.
|
196
|
-
transformation
|
166
|
+
it "should allow updating transformation allowed_for_strict" do
|
167
|
+
@api.update_transformation("c_scale,w_100", :allowed_for_strict => true)
|
168
|
+
transformation = @api.transformation("c_scale,w_100")
|
169
|
+
expect(transformation).not_to be_blank
|
170
|
+
expect(transformation["allowed_for_strict"]).to eq(true)
|
171
|
+
@api.update_transformation("c_scale,w_100", :allowed_for_strict => false)
|
172
|
+
transformation = @api.transformation("c_scale,w_100")
|
197
173
|
expect(transformation).not_to be_blank
|
198
|
-
expect(transformation["
|
199
|
-
expect(transformation["used"]).to eq(false)
|
174
|
+
expect(transformation["allowed_for_strict"]).to eq(false)
|
200
175
|
end
|
201
176
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
177
|
+
describe "named transformations" do
|
178
|
+
it "should allow creating named transformation" do
|
179
|
+
public_id = "api_test_transformation_#{Time.now.to_i}"
|
180
|
+
@api.create_transformation(public_id, "crop" => "scale", "width" => 102, :tags => TEST_TAG)
|
181
|
+
transformation = @api.transformation(public_id)
|
182
|
+
expect(transformation).not_to be_blank
|
183
|
+
expect(transformation["allowed_for_strict"]).to eq(true)
|
184
|
+
expect(transformation["info"]).to eq(["crop" => "scale", "width" => 102])
|
185
|
+
expect(transformation["used"]).to eq(false)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should allow deleting named transformation" do
|
189
|
+
public_id = "api_test_transformation_#{Time.now.to_i}"
|
190
|
+
@api.create_transformation(public_id, "crop" => "scale", "width" => 103, :tags => TEST_TAG)
|
191
|
+
@api.transformation(public_id)
|
192
|
+
@api.delete_transformation(public_id)
|
193
|
+
expect { @api.transformation(public_id) }.to raise_error(Cloudinary::Api::NotFound)
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should allow unsafe update of named transformation" do
|
197
|
+
public_id = "api_test_transformation_#{Time.now.to_i}"
|
198
|
+
@api.create_transformation(public_id, "crop" => "scale", "width" => 102, :tags => TEST_TAG)
|
199
|
+
@api.update_transformation(public_id, :unsafe_update => { "crop" => "scale", "width" => 103 })
|
200
|
+
transformation = @api.transformation(public_id)
|
201
|
+
expect(transformation).not_to be_blank
|
202
|
+
expect(transformation["info"]).to eq(["crop" => "scale", "width" => 103])
|
203
|
+
expect(transformation["used"]).to eq(false)
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
it "should allow deleting implicit transformation" do
|
208
|
+
@api.transformation("c_scale,w_100")
|
209
|
+
@api.delete_transformation("c_scale,w_100")
|
210
|
+
expect { @api.transformation("c_scale,w_100") }.to raise_error(Cloudinary::Api::NotFound)
|
211
|
+
end
|
207
212
|
end
|
208
213
|
|
209
214
|
it "should allow creating and listing upload_presets", :upload_preset => true do
|
210
|
-
|
211
|
-
|
212
|
-
@api.create_upload_preset(:name =>
|
213
|
-
|
214
|
-
@api.
|
215
|
-
@api.
|
216
|
-
@api.delete_upload_preset("api_test_upload_preset3")
|
215
|
+
name = []
|
216
|
+
3.times { |i| name.push( "api_test_#{Time.now.to_i}_#{i}")}
|
217
|
+
@api.create_upload_preset(:name => name[0], :folder => "folder", :tags => TEST_TAG)
|
218
|
+
@api.create_upload_preset(:name => name[1], :folder => "folder2", :tags => TEST_TAG)
|
219
|
+
@api.create_upload_preset(:name => name[2], :folder => "folder3", :tags => TEST_TAG)
|
220
|
+
expect(@api.upload_presets["presets"].first(3).map{|p| p["name"]}).to match_array(name)
|
217
221
|
end
|
218
222
|
|
219
223
|
it "should allow getting a single upload_preset", :upload_preset => true do
|
220
|
-
result = @api.create_upload_preset(:unsigned => true, :folder => "folder", :width => 100, :crop => :scale, :tags => ["a","b","c"], :context => {:a => "b", :c => "d"})
|
224
|
+
result = @api.create_upload_preset(:unsigned => true, :folder => "folder", :width => 100, :crop => :scale, :tags => ["a","b","c", TEST_TAG], :context => {:a => "b", :c => "d"})
|
221
225
|
name = result["name"]
|
222
226
|
preset = @api.upload_preset(name)
|
223
227
|
expect(preset["name"]).to eq(name)
|
@@ -225,31 +229,29 @@ describe Cloudinary::Api do
|
|
225
229
|
expect(preset["settings"]["folder"]).to eq("folder")
|
226
230
|
expect(preset["settings"]["transformation"]).to eq([{"width" => 100, "crop" => "scale"}])
|
227
231
|
expect(preset["settings"]["context"]).to eq({"a" => "b", "c" => "d"})
|
228
|
-
expect(preset["settings"]["tags"]).to eq(["a","b","c"])
|
229
|
-
@api.delete_upload_preset(name)
|
232
|
+
expect(preset["settings"]["tags"]).to eq(["a","b","c", TEST_TAG])
|
230
233
|
end
|
231
234
|
|
232
235
|
it "should allow deleting upload_presets", :upload_preset => true do
|
233
|
-
@api.create_upload_preset(:name => "api_test_upload_preset4", :folder => "folder")
|
236
|
+
@api.create_upload_preset(:name => "api_test_upload_preset4", :folder => "folder", :tags => TEST_TAG)
|
234
237
|
preset = @api.upload_preset("api_test_upload_preset4")
|
235
238
|
@api.delete_upload_preset("api_test_upload_preset4")
|
236
239
|
expect{preset = @api.upload_preset("api_test_upload_preset4")}.to raise_error
|
237
240
|
end
|
238
241
|
|
239
242
|
it "should allow updating upload_presets", :upload_preset => true do
|
240
|
-
name = @api.create_upload_preset(:folder => "folder")["name"]
|
243
|
+
name = @api.create_upload_preset(:folder => "folder", :tags => TEST_TAG)["name"]
|
241
244
|
preset = @api.upload_preset(name)
|
242
245
|
@api.update_upload_preset(name, preset["settings"].merge(:colors => true, :unsigned => true, :disallow_public_id => true))
|
243
246
|
preset = @api.upload_preset(name)
|
244
247
|
expect(preset["name"]).to eq(name)
|
245
248
|
expect(preset["unsigned"]).to eq(true)
|
246
|
-
expect(preset["settings"]).to eq({"folder" => "folder", "colors" => true, "disallow_public_id" => true})
|
247
|
-
@api.delete_upload_preset(name)
|
249
|
+
expect(preset["settings"]).to eq({"folder" => "folder", "colors" => true, "disallow_public_id" => true, "tags" => [TEST_TAG]})
|
248
250
|
end
|
249
251
|
|
250
252
|
# this test must be last because it deletes (potentially) all dependent transformations which some tests rely on. Excluded by default.
|
251
|
-
|
252
|
-
Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test5", :eager=>[:width=>101,:crop=>:scale])
|
253
|
+
skip "should allow deleting all resources", :delete_all=>true do
|
254
|
+
Cloudinary::Uploader.upload("spec/logo.png", :public_id=>"api_test5", :eager=>[:width=>101,:crop=>:scale], :tags => TEST_TAG)
|
253
255
|
resource = @api.resource("api_test5")
|
254
256
|
expect(resource).not_to be_blank
|
255
257
|
expect(resource["derived"].length).to eq(1)
|
@@ -260,7 +262,7 @@ describe Cloudinary::Api do
|
|
260
262
|
end
|
261
263
|
|
262
264
|
it "should support setting manual moderation status" do
|
263
|
-
result = Cloudinary::Uploader.upload("spec/logo.png", {:moderation => :manual})
|
265
|
+
result = Cloudinary::Uploader.upload("spec/logo.png", {:moderation => :manual, :tags => TEST_TAG})
|
264
266
|
expect(result["moderation"][0]["status"]).to eq("pending")
|
265
267
|
expect(result["moderation"][0]["kind"]).to eq("manual")
|
266
268
|
api_result = Cloudinary::Api.update(result["public_id"], {:moderation_status => :approved})
|
@@ -269,29 +271,29 @@ describe Cloudinary::Api do
|
|
269
271
|
end
|
270
272
|
|
271
273
|
it "should support requesting raw conversion" do
|
272
|
-
result = Cloudinary::Uploader.upload("spec/docx.docx", :resource_type => :raw)
|
274
|
+
result = Cloudinary::Uploader.upload("spec/docx.docx", :resource_type => :raw, :tags => TEST_TAG)
|
273
275
|
expect{Cloudinary::Api.update(result["public_id"], {:resource_type => :raw, :raw_convert => :illegal})}.to raise_error(Cloudinary::Api::BadRequest, /^Illegal value|not a valid/)
|
274
276
|
end
|
275
277
|
|
276
278
|
it "should support requesting categorization" do
|
277
|
-
result = Cloudinary::Uploader.upload("spec/logo.png")
|
279
|
+
result = Cloudinary::Uploader.upload("spec/logo.png", :tags => TEST_TAG)
|
278
280
|
expect{Cloudinary::Api.update(result["public_id"], {:categorization => :illegal})}.to raise_error(Cloudinary::Api::BadRequest, /^Illegal value/)
|
279
281
|
end
|
280
282
|
|
281
283
|
it "should support requesting detection" do
|
282
|
-
result = Cloudinary::Uploader.upload("spec/logo.png")
|
284
|
+
result = Cloudinary::Uploader.upload("spec/logo.png", :tags => TEST_TAG)
|
283
285
|
expect{Cloudinary::Api.update(result["public_id"], {:detection => :illegal})}.to raise_error(Cloudinary::Api::BadRequest, /^Illegal value/)
|
284
286
|
end
|
285
287
|
|
286
288
|
it "should support requesting auto_tagging" do
|
287
|
-
result = Cloudinary::Uploader.upload("spec/logo.png")
|
289
|
+
result = Cloudinary::Uploader.upload("spec/logo.png", :tags => TEST_TAG)
|
288
290
|
expect{Cloudinary::Api.update(result["public_id"], {:auto_tagging => 0.5})}.to raise_error(Cloudinary::Api::BadRequest, /^Must use/)
|
289
291
|
end
|
290
292
|
|
291
293
|
it "should support listing by moderation kind and value" do
|
292
|
-
result1 = Cloudinary::Uploader.upload("spec/logo.png", {:moderation => :manual})
|
293
|
-
result2 = Cloudinary::Uploader.upload("spec/logo.png", {:moderation => :manual})
|
294
|
-
result3 = Cloudinary::Uploader.upload("spec/logo.png", {:moderation => :manual})
|
294
|
+
result1 = Cloudinary::Uploader.upload("spec/logo.png", { :moderation => :manual, :tags => TEST_TAG})
|
295
|
+
result2 = Cloudinary::Uploader.upload("spec/logo.png", { :moderation => :manual, :tags => TEST_TAG})
|
296
|
+
result3 = Cloudinary::Uploader.upload("spec/logo.png", { :moderation => :manual, :tags => TEST_TAG})
|
295
297
|
Cloudinary::Api.update(result1["public_id"], {:moderation_status => :approved})
|
296
298
|
Cloudinary::Api.update(result2["public_id"], {:moderation_status => :rejected})
|
297
299
|
approved = Cloudinary::Api.resources_by_moderation(:manual, :approved, :max_results => 1000)["resources"].map{|r| r["public_id"]}
|
@@ -309,20 +311,71 @@ describe Cloudinary::Api do
|
|
309
311
|
end
|
310
312
|
|
311
313
|
it "should support listing folders" do
|
312
|
-
pending("For this test to work, 'Auto-create folders' should be enabled in the Upload Settings, " +
|
313
|
-
|
314
|
-
|
315
|
-
Cloudinary::Uploader.upload("spec/logo.png", {:public_id => "test_folder1/item"})
|
316
|
-
Cloudinary::Uploader.upload("spec/logo.png", {:public_id => "test_folder2/item"})
|
317
|
-
Cloudinary::Uploader.upload("spec/logo.png", {:public_id => "test_folder1/test_subfolder1/item"})
|
318
|
-
Cloudinary::Uploader.upload("spec/logo.png", {:public_id => "test_folder1/test_subfolder2/item"})
|
314
|
+
# pending("For this test to work, 'Auto-create folders' should be enabled in the Upload Settings, " +
|
315
|
+
# "and the account should be empty of folders. " +
|
316
|
+
# "Comment out this line if you really want to test it.")
|
317
|
+
Cloudinary::Uploader.upload("spec/logo.png", {:public_id => "test_folder1/item", :tags => TEST_TAG})
|
318
|
+
Cloudinary::Uploader.upload("spec/logo.png", {:public_id => "test_folder2/item", :tags => TEST_TAG})
|
319
|
+
Cloudinary::Uploader.upload("spec/logo.png", {:public_id => "test_folder1/test_subfolder1/item", :tags => TEST_TAG})
|
320
|
+
Cloudinary::Uploader.upload("spec/logo.png", {:public_id => "test_folder1/test_subfolder2/item", :tags => TEST_TAG})
|
319
321
|
result = Cloudinary::Api.root_folders
|
320
|
-
expect(result["folders"]
|
321
|
-
|
322
|
+
expect(result["folders"]).not_to be_empty, "Folders should have been created. Make sure that 'Auto-create folders' is enabled for this cloud"
|
323
|
+
names = result["folders"].map{|f| f["name"]}
|
324
|
+
expect(names).to include("test_folder1")
|
325
|
+
expect(names).to include("test_folder2")
|
322
326
|
result = Cloudinary::Api.subfolders("test_folder1")
|
323
|
-
|
324
|
-
|
327
|
+
|
328
|
+
paths = result["folders"].map{|f| f["path"]}
|
329
|
+
expect(paths).to include("test_folder1/test_subfolder1")
|
330
|
+
expect(paths).to include("test_folder1/test_subfolder2")
|
325
331
|
expect{Cloudinary::Api.subfolders("test_folder")}.to raise_error(Cloudinary::Api::NotFound)
|
326
|
-
|
332
|
+
end
|
333
|
+
|
334
|
+
describe '.restore' do
|
335
|
+
before :each do
|
336
|
+
Cloudinary::Uploader.upload("spec/logo.png", :public_id => "api_test_restore", :backup => true, :tags => TEST_TAG)
|
337
|
+
resource = Cloudinary::Api.resource("api_test_restore")
|
338
|
+
expect(resource).not_to be_nil
|
339
|
+
expect(resource["bytes"]).to eq(3381)
|
340
|
+
Cloudinary::Api.delete_resources("api_test_restore")
|
341
|
+
resource = Cloudinary::Api.resource("api_test_restore")
|
342
|
+
expect(resource).not_to be_nil
|
343
|
+
expect(resource["bytes"]).to eq(0)
|
344
|
+
expect(resource["placeholder"]).to eq(true)
|
345
|
+
end
|
346
|
+
it 'should restore a deleted resource' do
|
347
|
+
response = Cloudinary::Api.restore("api_test_restore")
|
348
|
+
info = response["api_test_restore"]
|
349
|
+
expect(info).not_to be_nil
|
350
|
+
expect(info["bytes"]).to eq(3381)
|
351
|
+
resource = Cloudinary::Api.resource("api_test_restore")
|
352
|
+
expect(resource).not_to be_nil
|
353
|
+
expect(resource["bytes"]).to eq(3381)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
describe 'mapping' do
|
358
|
+
mapping = "api_test_upload_mapping#{rand(100000)}"
|
359
|
+
after :all do
|
360
|
+
begin
|
361
|
+
Cloudinary::Api.delete_upload_mapping(mapping)
|
362
|
+
rescue
|
363
|
+
puts "Could not delete #{mapping}"
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
it 'should create mapping' do
|
368
|
+
result = Cloudinary::Api.create_upload_mapping(mapping, :template =>"http://cloudinary.com")
|
369
|
+
result = Cloudinary::Api.upload_mapping(mapping)
|
370
|
+
expect(result['template']).to eq("http://cloudinary.com")
|
371
|
+
Cloudinary::Api.update_upload_mapping(mapping, "template" =>"http://res.cloudinary.com")
|
372
|
+
result = Cloudinary::Api.upload_mapping(mapping)
|
373
|
+
expect(result["template"]).to eq("http://res.cloudinary.com")
|
374
|
+
result = Cloudinary::Api.upload_mappings
|
375
|
+
expect(result["mappings"]).to include("folder" => mapping, "template" =>"http://res.cloudinary.com" )
|
376
|
+
Cloudinary::Api.delete_upload_mapping(mapping)
|
377
|
+
result = Cloudinary::Api.upload_mappings()
|
378
|
+
expect(result["mappings"]).not_to include(mapping )
|
379
|
+
end
|
327
380
|
end
|
328
381
|
end
|