cloudinary 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|