Skip to content

Commit

Permalink
Fix googleapis#64 - Add Bucket.copy_key() and Key.rename() methods.
Browse files Browse the repository at this point in the history
  • Loading branch information
gsalgado committed Jun 8, 2014
1 parent af92e74 commit 1c80d61
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 2 deletions.
24 changes: 22 additions & 2 deletions gcloud/storage/bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,28 @@ def delete_keys(self, keys):
for key in keys:
self.delete_key(key)

def copy_key(self):
raise NotImplementedError
def copy_key(self, key, destination_bucket, new_name=None):
"""Copy the given key to the given bucket, optionally with a new name.
:type key: string or :class:`gcloud.storage.key.Key`
:param key: The key to be copied.
:type destination_bucket: :class:`gcloud.storage.bucket.Bucket`
:param destination_bucket: The bucket where the key should be copied into.
:type new_name: string
:param new_name: (optional) the new name for the copied file.
:rtype: :class:`gcloud.storage.key.Key`
:returns: The new Key.
"""
# TODO: Check that the user has WRITE permissions on the dst bucket?
if new_name is None:
new_name = key.name
new_key = destination_bucket.new_key(new_name)
api_path = key.path + '/copyTo' + new_key.path
self.connection.api_request(method='POST', path=api_path)
return new_key

def upload_file(self, filename, key=None):
# TODO: What do we do about overwriting data?
Expand Down
13 changes: 13 additions & 0 deletions gcloud/storage/key.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,19 @@ def exists(self):

return self.bucket.get_key(self.name) is not None

def rename(self, new_name):
"""Renames this key.
:type new_name: string
:param new_name: The new name for this key.
"""
new_key = self.bucket.copy_key(self, self.bucket, new_name)
self.delete()
# This feels like a dirty hack, but since the bucket is the same we can
# just change the name attribute of this instance to have it point to the
# new key.
self.name = new_key.name

This comment has been minimized.

Copy link
@aliafshar

aliafshar Jun 9, 2014

How about returning the new key as an alternative to modifying the state of this one?

This comment has been minimized.

Copy link
@gsalgado

gsalgado via email Jun 9, 2014

Author Owner

This comment has been minimized.

Copy link
@aliafshar

aliafshar Jun 9, 2014

Agreed, this approach is likely best. @jgeewax, I couldn't see anything, but can you anticipate any problems from this?


def delete(self):
"""Deletes a key from Cloud Storage.
Expand Down
36 changes: 36 additions & 0 deletions gcloud/storage/test_key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import unittest2

from gcloud.storage.bucket import Bucket
from gcloud.storage.connection import Connection
from gcloud.storage.key import Key


class TestKey(unittest2.TestCase):

def setUp(self):
# Mock Connection.api_request with a method that just stores the HTTP
# method, path and query params in an instance variable for later
# inspection.
# TODO: It'd be better to make the Connection talk to a local HTTP server
# that we can inspect, but a simple test using a mock is certainly better
# than no tests.
self.connection = Connection('project-name')
self.connection.api_request = self.mock_api_request
self.api_request_calls = []

def mock_api_request(self, method, path=None, query_params=None,
data=None, content_type=None,
api_base_url=None, api_version=None,
expect_json=True):
self.api_request_calls.append([method, path, query_params])

def test_rename(self):
bucket = Bucket(self.connection, 'bucket')
key = Key(bucket, 'key')
orig_key_path = key.path
key.rename('new-name')
expected = [
['POST', orig_key_path + '/copyTo/b/bucket/o/new-name', None],
['DELETE', orig_key_path, None]]
self.assertEqual(key.name, 'new-name')
self.assertEqual(self.api_request_calls, expected)

0 comments on commit 1c80d61

Please sign in to comment.