Skip to content

Commit 7e2a504

Browse files
author
RTLcoil
authored
Add support for download_backedup_asset helper method
1 parent 0e40ca7 commit 7e2a504

File tree

4 files changed

+187
-9
lines changed

4 files changed

+187
-9
lines changed

lib/cloudinary/api.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,16 @@ def self.resource(public_id, options={})
8282
:phash,
8383
:quality_analysis,
8484
:derived_next_cursor,
85-
:accessibility_analysis
85+
:accessibility_analysis,
86+
:versions
8687
), options)
8788
end
8889

8990
def self.restore(public_ids, options={})
9091
resource_type = options[:resource_type] || "image"
9192
type = options[:type] || "upload"
9293
uri = "resources/#{resource_type}/#{type}/restore"
93-
call_api(:post, uri, { :public_ids => public_ids }, options)
94+
call_api(:post, uri, { :public_ids => public_ids, :versions => options[:versions] }, options)
9495
end
9596

9697
def self.update(public_id, options={})

lib/cloudinary/utils.rb

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ class Cloudinary::Utils
159159
LONG_URL_SIGNATURE_LENGTH = 32
160160
SHORT_URL_SIGNATURE_LENGTH = 8
161161

162+
UPLOAD_PREFIX = 'https://api.cloudinary.com'
163+
162164
ALGO_SHA1 = :sha1
163165
ALGO_SHA256 = :sha256
164166

@@ -688,11 +690,23 @@ def self.unsigned_download_url_prefix(source, cloud_name, private_cdn, cdn_subdo
688690
prefix
689691
end
690692

693+
# Creates a base URL for the cloudinary api
694+
#
695+
# @param [Object] path Resource name
696+
# @param [Hash] options Additional options
697+
#
698+
# @return [String]
699+
def self.base_api_url(path, options = {})
700+
cloudinary = options[:upload_prefix] || Cloudinary.config.upload_prefix || UPLOAD_PREFIX
701+
cloud_name = options[:cloud_name] || Cloudinary.config.cloud_name || raise(CloudinaryException, 'Must supply cloud_name')
702+
703+
[cloudinary, 'v1_1', cloud_name, path].join('/')
704+
end
705+
691706
def self.cloudinary_api_url(action = 'upload', options = {})
692-
cloudinary = options[:upload_prefix] || Cloudinary.config.upload_prefix || "https://api.cloudinary.com"
693-
cloud_name = options[:cloud_name] || Cloudinary.config.cloud_name || raise(CloudinaryException, "Must supply cloud_name")
694-
resource_type = options[:resource_type] || "image"
695-
return [cloudinary, "v1_1", cloud_name, resource_type, action].join("/")
707+
resource_type = options[:resource_type] || 'image'
708+
709+
base_api_url([resource_type, action], options)
696710
end
697711

698712
def self.sign_request(params, options={})
@@ -1177,6 +1191,25 @@ def self.is_remote?(url)
11771191
REMOTE_URL_REGEX === url
11781192
end
11791193

1194+
# The returned url should allow downloading the backedup asset based on the version and asset id
1195+
#
1196+
# asset and version id are returned with resource(<PUBLIC_ID1>, { versions: true })
1197+
#
1198+
# @param [String] asset_id Asset identifier
1199+
# @param [String] version_id Specific version of asset to download
1200+
# @param [Hash] options Additional options
1201+
#
1202+
# @return [String] An url for downloading a file
1203+
def self.download_backedup_asset(asset_id, version_id, options = {})
1204+
params = Cloudinary::Utils.sign_request({
1205+
:timestamp => (options[:timestamp] || Time.now.to_i),
1206+
:asset_id => asset_id,
1207+
:version_id => version_id
1208+
}, options)
1209+
1210+
"#{Cloudinary::Utils.base_api_url("download_backup", options)}?#{Cloudinary::Utils.hash_query_params((params))}"
1211+
end
1212+
11801213
# Format date in a format accepted by the usage API (e.g., 31-12-2020) if
11811214
# passed value is of type Date, otherwise return the string representation of
11821215
# the input.

spec/api_spec.rb

Lines changed: 138 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
describe Cloudinary::Api do
55
break puts("Please setup environment for api test to run") if Cloudinary.config.api_secret.blank?
66
include_context "cleanup", TIMESTAMP_TAG
7+
8+
prefix = "api_test_#{SUFFIX}"
9+
710
TEST_WIDTH = rand(1000)
811
TEST_TRANSFOMATION = "c_scale,w_#{TEST_WIDTH}"
9-
prefix = "api_test_#{SUFFIX}"
12+
PUBLIC_ID_BACKUP_1 = "#{prefix}backup_1#{Time.now.to_i}"
13+
PUBLIC_ID_BACKUP_2 = "#{prefix}backup_2#{Time.now.to_i}"
14+
1015
test_id_1 = "#{prefix}_1"
1116
test_id_2 = "#{prefix}_2"
1217
test_id_3 = "#{prefix}_3"
@@ -230,6 +235,28 @@
230235
expect(tags).to be_blank
231236
end
232237

238+
describe "backup resource" do
239+
let(:public_id) { "api_test_backup_#{SUFFIX}" }
240+
241+
before(:each) do
242+
Cloudinary::Uploader.upload(TEST_IMG, :tags => [TEST_TAG, TIMESTAMP_TAG], :public_id => public_id, :backup => true)
243+
response = @api.resource(public_id)
244+
expect(response).not_to be_nil
245+
end
246+
247+
it "should return the asset details together with all of its backed up versions when versions is true" do
248+
resource = @api.resource(public_id, :versions => true)
249+
250+
expect(resource["versions"]).to be_an_instance_of(Array)
251+
end
252+
253+
it "should return the asset details together without backed up versions when versions is false" do
254+
resource = @api.resource(public_id, :versions => false)
255+
256+
expect(resource["versions"]).to be_nil
257+
end
258+
end
259+
233260
describe 'transformations' do
234261
it "should allow listing transformations" do
235262
transformations = @api.transformations()["transformations"]
@@ -507,9 +534,117 @@
507534
end
508535

509536
describe '.restore' do
537+
let(:public_id) { "api_test_restore#{SUFFIX}" }
538+
539+
before(:each) do
540+
Cloudinary::Uploader.upload(TEST_IMG, :tags => [TEST_TAG, TIMESTAMP_TAG], :public_id => public_id, :backup => true)
541+
sleep(2)
542+
543+
resource = @api.resource(public_id)
544+
expect(resource).not_to be_nil
545+
expect(resource["bytes"]).to eq(3381)
546+
547+
@api.delete_resources(public_id)
548+
549+
resource = @api.resource(public_id)
550+
expect(resource).not_to be_nil
551+
expect(resource["bytes"]).to eq(0)
552+
expect(resource["placeholder"]).to eq(true)
553+
end
554+
510555
it 'should restore a deleted resource' do
511-
expect(RestClient::Request).to receive(:execute).with(deep_hash_value( [:payload, :public_ids] => "api_test_restore", [:url] => /.*\/restore$/))
512-
Cloudinary::Api.restore("api_test_restore")
556+
response = @api.restore([public_id])
557+
558+
info = response[public_id]
559+
expect(info).not_to be_nil
560+
expect(info["bytes"]).to eq(3381)
561+
562+
resource = @api.resource(public_id)
563+
expect(resource).not_to be_nil
564+
expect(resource["bytes"]).to eq(3381)
565+
end
566+
567+
it "should restore different versions of a deleted asset" do
568+
# Upload the same file twice (upload->delete->upload->delete)
569+
570+
# Upload and delete a file
571+
first_upload = Cloudinary::Uploader.upload(TEST_IMG, :tags => [TEST_TAG, TIMESTAMP_TAG], :public_id => PUBLIC_ID_BACKUP_1, :backup => true)
572+
sleep(1)
573+
574+
first_delete = @api.delete_resources([PUBLIC_ID_BACKUP_1])
575+
576+
# Upload and delete it again, this time add angle to create a different 'version'
577+
second_upload = Cloudinary::Uploader.upload(TEST_IMG, :tags => [TEST_TAG, TIMESTAMP_TAG], :public_id => PUBLIC_ID_BACKUP_1, :transformation => { :angle => 0 }, :backup => true)
578+
sleep(1)
579+
580+
second_delete = @api.delete_resources([PUBLIC_ID_BACKUP_1])
581+
sleep(1)
582+
583+
# Ensure all files were uploaded correctly
584+
expect(first_upload).not_to be_nil
585+
expect(second_upload).not_to be_nil
586+
587+
# Sanity, ensure these uploads are different before we continue
588+
expect(first_upload["bytes"]).not_to equal(second_upload["bytes"])
589+
590+
# Ensure all files were deleted correctly
591+
expect(first_delete).to have_key("deleted")
592+
expect(second_delete).to have_key("deleted")
593+
594+
# Get the versions of the deleted asset
595+
get_versions_resp = @api.resource(PUBLIC_ID_BACKUP_1, :versions => true)
596+
597+
first_asset_version = get_versions_resp["versions"][0]["version_id"]
598+
second_asset_version = get_versions_resp["versions"][1]["version_id"]
599+
600+
# Restore first version, ensure it's equal to the upload size
601+
sleep(1)
602+
first_ver_restore = @api.restore([PUBLIC_ID_BACKUP_1], :versions => [first_asset_version])
603+
expect(first_ver_restore[PUBLIC_ID_BACKUP_1]["bytes"]).to eq(first_upload["bytes"])
604+
605+
# Restore second version, ensure it's equal to the upload size
606+
sleep(1)
607+
second_ver_restore = @api.restore([PUBLIC_ID_BACKUP_1], { :versions => [second_asset_version] })
608+
expect(second_ver_restore[PUBLIC_ID_BACKUP_1]["bytes"]).to eq(second_upload["bytes"])
609+
610+
# Cleanup
611+
final_delete_resp = @api.delete_resources([PUBLIC_ID_BACKUP_1])
612+
expect(final_delete_resp).to have_key("deleted")
613+
end
614+
615+
it "should restore two different deleted assets" do
616+
# Upload two different files
617+
first_upload = Cloudinary::Uploader.upload(TEST_IMG, :tags => [TEST_TAG, TIMESTAMP_TAG], :public_id => PUBLIC_ID_BACKUP_1, :backup => true)
618+
second_upload = Cloudinary::Uploader.upload(TEST_IMG, :tags => [TEST_TAG, TIMESTAMP_TAG], :public_id => PUBLIC_ID_BACKUP_2, :transformation => { :angle => 0 }, :backup => true)
619+
620+
# delete both resources
621+
delete_all = @api.delete_resources([PUBLIC_ID_BACKUP_1, PUBLIC_ID_BACKUP_2])
622+
623+
# Expect correct deletion of the assets
624+
expect(delete_all["deleted"][PUBLIC_ID_BACKUP_1]).to eq("deleted")
625+
expect(delete_all["deleted"][PUBLIC_ID_BACKUP_2]).to eq("deleted")
626+
627+
get_first_asset_version = @api.resource(PUBLIC_ID_BACKUP_1, :versions => true)
628+
get_second_asset_version = @api.resource(PUBLIC_ID_BACKUP_2, :versions => true)
629+
630+
first_asset_version = get_first_asset_version["versions"][0]["version_id"]
631+
second_asset_version = get_second_asset_version["versions"][0]["version_id"]
632+
633+
ids_to_restore = [PUBLIC_ID_BACKUP_1, PUBLIC_ID_BACKUP_2]
634+
versions_to_restore = [first_asset_version, second_asset_version]
635+
636+
restore = @api.restore(ids_to_restore, :versions => versions_to_restore)
637+
638+
# Expect correct restorations
639+
expect(restore[PUBLIC_ID_BACKUP_1]["bytes"]).to eq(first_upload["bytes"])
640+
expect(restore[PUBLIC_ID_BACKUP_2]["bytes"]).to eq(second_upload["bytes"])
641+
642+
# Cleanup
643+
final_delete = @api.delete_resources([PUBLIC_ID_BACKUP_1, PUBLIC_ID_BACKUP_2])
644+
645+
# Expect correct deletion of the assets
646+
expect(final_delete["deleted"][PUBLIC_ID_BACKUP_1]).to eq("deleted")
647+
expect(final_delete["deleted"][PUBLIC_ID_BACKUP_2]).to eq("deleted")
513648
end
514649
end
515650

spec/archive_spec.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@
6161
end
6262
end
6363
end
64+
65+
describe "download_backedup_asset" do
66+
it "should return url with asset and version id" do
67+
download_backedup_asset_url = Cloudinary::Utils.download_backedup_asset("b71b23d9c89a81a254b88a91a9dad8cd", "0e493356d8a40b856c4863c026891a4e")
68+
69+
expect(download_backedup_asset_url).to include("asset_id")
70+
expect(download_backedup_asset_url).to include("version_id")
71+
end
72+
end
6473
end
6574

6675
describe Cloudinary::Uploader do

0 commit comments

Comments
 (0)