Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions google-cloud-storage/lib/google/cloud/storage/bucket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3143,6 +3143,51 @@ def reload!
end
alias refresh! reload!

##
# Moves File from source to destination path within the same HNS-enabled bucket
# This Operation is being performed at server side
# @param [String] source_file The file name in existing bucket
# @param [String] destination_file The new filename to be created on bucket
# If the destination path includes non-existent parent folders, they will be created.
# @example
# require "google/cloud/storage"
# storage = Google::Cloud::Storage.new
# bucket = storage.bucket bucket_name, skip_lookup: true
# bucket.move_file source_file_name, destination_file_name
def move_file source_file,
destination_file,
if_generation_match: nil,
if_generation_not_match: nil,
if_metageneration_match: nil,
if_metageneration_not_match: nil,
if_source_generation_match: nil,
if_source_generation_not_match: nil,
if_source_metageneration_match: nil,
if_source_metageneration_not_match: nil,
user_project: nil,
fields: nil,
quota_user: nil,
user_ip: nil,
options: {}
ensure_service!
service.move_file name,
source_file,
destination_file,
if_generation_match: if_generation_match,
if_generation_not_match: if_generation_not_match,
if_metageneration_match: if_metageneration_match,
if_metageneration_not_match: if_metageneration_not_match,
if_source_generation_match: if_source_generation_match,
if_source_generation_not_match: if_source_generation_not_match,
if_source_metageneration_match: if_source_metageneration_match,
if_source_metageneration_not_match: if_source_metageneration_not_match,
user_project: user_project,
fields: fields,
quota_user: quota_user,
user_ip: user_ip,
options: options
end

##
# Determines whether the bucket exists in the Storage service.
#
Expand Down
38 changes: 38 additions & 0 deletions google-cloud-storage/lib/google/cloud/storage/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,44 @@ def patch_file bucket_name,
end
end

##
# Moves file from source to destination path within bucket
def move_file name,
source_file,
destination_file,
if_generation_match: nil,
if_generation_not_match: nil,
if_metageneration_match: nil,
if_metageneration_not_match: nil,
if_source_generation_match: nil,
if_source_generation_not_match: nil,
if_source_metageneration_match: nil,
if_source_metageneration_not_match: nil,
user_project: nil,
fields: nil,
quota_user: nil,
user_ip: nil,
options: {}
execute do
service.move_object name,
source_file,
destination_file,
if_generation_match: if_generation_match,
if_generation_not_match: if_generation_not_match,
if_metageneration_match: if_metageneration_match,
if_metageneration_not_match: if_metageneration_not_match,
if_source_generation_match: if_source_generation_match,
if_source_generation_not_match: if_source_generation_not_match,
if_source_metageneration_match: if_source_metageneration_match,
if_source_metageneration_not_match: if_source_metageneration_not_match,
user_project: user_project(user_project),
fields: fields,
quota_user: quota_user,
user_ip: user_ip,
options: options
end
end

##
# Permanently deletes a file.
def delete_file bucket_name,
Expand Down
35 changes: 35 additions & 0 deletions google-cloud-storage/samples/acceptance/buckets_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
require_relative "../storage_set_retention_policy"
require_relative "../storage_get_autoclass"
require_relative "../storage_set_autoclass"
require_relative "../storage_move_object"

describe "Buckets Snippets" do
let(:storage_client) { Google::Cloud::Storage.new }
Expand Down Expand Up @@ -581,4 +582,38 @@
bucket.public_access_prevention = :inherited
end
end

describe "storage move file" do
let(:source_file) { "file_1_name_#{SecureRandom.hex}.txt" }
let(:destination_file) { "file_2_name_#{SecureRandom.hex}.txt" }
let :hns_bucket do
hierarchical_namespace = Google::Apis::StorageV1::Bucket::HierarchicalNamespace.new enabled: true
storage_client.create_bucket random_bucket_name do |b|
b.uniform_bucket_level_access = true
b.hierarchical_namespace = hierarchical_namespace
end
end
let :create_source_file do
file_content = "A" * (3 * 1024 * 1024) # 3 MB of 'A' characters
file = StringIO.new file_content
hns_bucket.create_file file, source_file
end
it "file is moved and old file is deleted" do
create_source_file
out, _err = capture_io do
move_object bucket_name: hns_bucket.name, source_file_name: source_file, destination_file_name: destination_file
end
assert_includes out, "New File #{destination_file} created\n"
refute_nil(hns_bucket.file(destination_file))
assert_nil(hns_bucket.file(source_file))
end

it "raises error if source and destination are having same filename" do
create_source_file
exception = assert_raises Google::Cloud::InvalidArgumentError do
move_object bucket_name: hns_bucket.name, source_file_name: source_file, destination_file_name: source_file
end
assert_equal "invalid: Source and destination object names must be different.", exception.message
end
end
end
40 changes: 40 additions & 0 deletions google-cloud-storage/samples/storage_move_object.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# [START storage_move_object]
def move_object bucket_name:, source_file_name:, destination_file_name:
# The ID of your GCS bucket
# bucket_name = "your-unique-bucket-name"

# The name of your GCS object
# source_file_name = "your-file-name"

# The new object name which you want to craete
# destination_file_name = "your-new-file-name"

require "google/cloud/storage"

storage = Google::Cloud::Storage.new
bucket = storage.bucket bucket_name, skip_lookup: true

bucket.move_file source_file_name, destination_file_name
fetch_file = bucket.file destination_file_name
puts "New File #{fetch_file.name} created\n"
end
# [END storage_move_object]

if $PROGRAM_NAME == __FILE__
move_object bucket_name: ARGV.shift, source_file_name: ARGV.shift,
destination_file_name: ARGV.shift
end
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ def remove_bucket_conditional_iam_binding bucket_name:
description: description,
expression: expression
}
if (b.role == role) && (b.condition &&
b.condition.title == title &&
b.condition.description == description &&
b.condition.expression == expression)
if b.role == role && b.condition &&
b.condition.title == title &&
b.condition.description == description &&
b.condition.expression == expression
binding_to_remove = b
end
end
Expand Down
26 changes: 26 additions & 0 deletions google-cloud-storage/test/google/cloud/storage/bucket_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1381,6 +1381,32 @@
_(bucket.api_url).must_equal "#{new_url_root}/b/#{bucket_name}"
mock.verify
end
describe "storage move file" do
it "moves a file for bucket" do
file_name = "file.ext"
file_2_name = "file1.ext"
mock = Minitest::Mock.new
mock.expect :move_object,Google::Apis::StorageV1::Object.from_json(random_file_hash(bucket.name, file_2_name).to_json),[ bucket.name, file_name,file_2_name],**move_object_args
bucket.service.mocked_service = mock
file = bucket.move_file file_name, file_2_name
mock.verify
_(file).must_be_kind_of Google::Apis::StorageV1::Object
end

it "raises error if source and destination are having same filename" do
file_name = "file.ext"
file_2_name = "file1.ext"
mock = Minitest::Mock.new
mock.expect :move_object, [ bucket.name, file_name,file_name] do
raise Google::Cloud::InvalidArgumentError, "invalid: Source and destination object names must be different."
end
bucket.service.mocked_service = mock
exception = assert_raises(Google::Cloud::InvalidArgumentError) do
bucket.move_file file_name, file_name
end
assert_equal "invalid: Source and destination object names must be different.", exception.message
end
end

def create_file_gapi bucket=nil, name = nil
Google::Apis::StorageV1::Object.from_json random_file_hash(bucket, name).to_json
Expand Down
30 changes: 30 additions & 0 deletions google-cloud-storage/test/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,36 @@ def patch_object_args generation: nil,
}
end

def move_object_args if_generation_match: nil,
if_generation_not_match: nil,
if_metageneration_match: nil,
if_metageneration_not_match: nil,
if_source_generation_match: nil,
if_source_generation_not_match: nil,
if_source_metageneration_match: nil,
if_source_metageneration_not_match: nil,
user_project: nil,
fields: nil,
quota_user: nil,
user_ip: nil,
options: {}
{
if_generation_match: if_generation_match,
if_generation_not_match: if_generation_not_match,
if_metageneration_match: if_metageneration_match,
if_metageneration_not_match: if_metageneration_not_match,
if_source_generation_match: if_source_generation_match,
if_source_generation_not_match: if_source_generation_not_match,
if_source_metageneration_match: if_source_metageneration_match,
if_source_metageneration_not_match: if_source_metageneration_not_match,
user_project: user_project,
fields: fields,
quota_user: quota_user,
user_ip: user_ip,
options: options
}
end

def delete_object_args generation: nil,
if_generation_match: nil,
if_generation_not_match: nil,
Expand Down