Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
feccf18
Storage: client redesign proposal
Jul 3, 2019
f9e6924
Merge branch 'client_redesign_create_bucket_7762' into redesign_clien…
Jul 3, 2019
1f57249
Delete unittest.main
Jul 3, 2019
b9146a4
Delete unnecessary import
Jul 3, 2019
be8166c
Cosmetic changes
Jul 3, 2019
c9ec3e9
Merge branch 'master' into redesign_client_create_bucket_7762
Jul 3, 2019
d1bafa1
Merge branch 'master' into redesign_client_create_bucket_7762
Jul 5, 2019
8c21c5a
Merge branch 'redesign_client_create_bucket_7762' of https://github.c…
Jul 5, 2019
8d31e55
Merge branch 'master' into redesign_client_create_bucket_7762
Jul 10, 2019
1c50d4c
Merge branch 'master' into redesign_client_create_bucket_7762
Aug 8, 2019
09d0c6f
Delete public property for bucket properties and mocks in bucket test…
Aug 8, 2019
b8e091f
Merge branch 'master' into redesign_client_create_bucket_7762
Oct 28, 2019
5a31265
Updated PRed changes to actualize them with changes from a79d98de3e9d…
Oct 28, 2019
e4f94da
Black reformat
Oct 28, 2019
e96d05c
Add user_project param into Client.create_bucket() method.
Nov 1, 2019
35427fb
Fix comment.
Nov 1, 2019
608d7ac
Merge branch 'master' of https://github.com/q-logic/google-cloud-pyth…
Nov 4, 2019
db2c0ae
Merge branch 'redesign_client_create_bucket_7762' of https://github.c…
Nov 4, 2019
8000ce6
Create bucket should support user_project.
Nov 4, 2019
e206be2
Test for user_project query parameter.
Nov 4, 2019
2cd2321
Merge branch 'master' into redesign_client_create_bucket_7762
Nov 5, 2019
675e11d
Copy tests from bucket test to client.
Nov 5, 2019
2530133
Revert "Copy tests from bucket test to client."
Nov 5, 2019
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
34 changes: 12 additions & 22 deletions storage/google/cloud/storage/bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,17 @@ def user_project(self):
"""
return self._user_project

@property
def properties(self):
"""Properties of the current bucket object.

Returns:
dict: Bucket properties transformed to dict.
"""
properties = {key: self._properties[key] for key in self._changes}
properties["name"] = self.name
return properties
Comment thread
IlyaFaer marked this conversation as resolved.
Outdated

def blob(
self,
blob_name,
Expand Down Expand Up @@ -577,28 +588,7 @@ def create(self, client=None, project=None, location=None):
raise ValueError("Cannot create bucket with 'user_project' set.")

client = self._require_client(client)

if project is None:
project = client.project

if project is None:
raise ValueError("Client project not set: pass an explicit project.")

query_params = {"project": project}
properties = {key: self._properties[key] for key in self._changes}
properties["name"] = self.name

if location is not None:
properties["location"] = location

api_response = client._connection.api_request(
method="POST",
path="/b",
query_params=query_params,
data=properties,
_target_object=self,
)
self._set_properties(api_response)
client.create_bucket(self, project=project, location=location)

def patch(self, client=None):
"""Sends all changed properties in a PATCH request.
Expand Down
29 changes: 27 additions & 2 deletions storage/google/cloud/storage/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ def lookup_bucket(self, bucket_name):
except NotFound:
return None

def create_bucket(self, bucket_or_name, requester_pays=None, project=None):
def create_bucket(self, bucket_or_name, requester_pays=None, project=None, location=None):
"""API call: create a new bucket via a POST request.

See
Expand All @@ -316,6 +316,10 @@ def create_bucket(self, bucket_or_name, requester_pays=None, project=None):
project (str):
Optional. the project under which the bucket is to be created.
If not passed, uses the project set on the client.
location (str):
Optional. The location of the bucket. If not passed,
the default location, US, will be used. See
https://cloud.google.com/storage/docs/bucket-locations

Returns:
google.cloud.storage.bucket.Bucket
Expand Down Expand Up @@ -348,9 +352,30 @@ def create_bucket(self, bucket_or_name, requester_pays=None, project=None):
"""
bucket = self._bucket_arg_to_bucket(bucket_or_name)

if project is None:
project = self.project

if project is None:
raise ValueError("Client project not set: pass an explicit project.")

if requester_pays is not None:
bucket.requester_pays = requester_pays
bucket.create(client=self, project=project)

query_params = {"project": project}
properties = bucket.properties

if location is not None:
properties["location"] = location

api_response = self._connection.api_request(
method="POST",
path="/b",
query_params=query_params,
data=properties,
_target_object=bucket,
)

bucket._set_properties(api_response)
return bucket

def download_blob_to_file(self, blob_or_uri, file_obj, start=None, end=None):
Expand Down
161 changes: 111 additions & 50 deletions storage/tests/unit/test_bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
import mock


def _make_connection(*responses):
import google.cloud.storage._http

mock_connection = mock.create_autospec(google.cloud.storage._http.Connection)
mock_connection.user_agent = "testing 1.2.3"
mock_connection.api_request.side_effect = list(responses)
return mock_connection

Comment thread
IlyaFaer marked this conversation as resolved.

def _create_signing_credentials():
import google.auth.credentials

Expand Down Expand Up @@ -535,66 +544,109 @@ def test_create_w_user_project(self):
bucket.create()

def test_create_w_missing_client_project(self):
from google.cloud.storage.client import Client

BUCKET_NAME = "bucket-name"
connection = _Connection()
client = _Client(connection, project=None)
bucket = self._make_one(client, BUCKET_NAME)
connection = _make_connection()
client = Client(project=None)

with self.assertRaises(ValueError):
bucket.create()
with mock.patch(
'google.cloud.storage.client.Client._connection',
new_callable=mock.PropertyMock
) as client_mock:
client_mock.return_value = connection
Comment thread
IlyaFaer marked this conversation as resolved.
Outdated

bucket = self._make_one(client, BUCKET_NAME)

with self.assertRaises(ValueError):
bucket.create()

def test_create_w_explicit_project(self):
from google.cloud.storage.client import Client

PROJECT = "PROJECT"
BUCKET_NAME = "bucket-name"
OTHER_PROJECT = "other-project-123"
DATA = {"name": BUCKET_NAME}
connection = _Connection(DATA)
client = _Client(connection, project=PROJECT)
bucket = self._make_one(client, BUCKET_NAME)

bucket.create(project=OTHER_PROJECT)

kw, = connection._requested
self.assertEqual(kw["method"], "POST")
self.assertEqual(kw["path"], "/b")
self.assertEqual(kw["query_params"], {"project": OTHER_PROJECT})
self.assertEqual(kw["data"], DATA)
connection = _make_connection(DATA)
client = Client(project=PROJECT)
with mock.patch(
'google.cloud.storage.client.Client._connection',
new_callable=mock.PropertyMock
) as client_mock:
client_mock.return_value = connection

bucket = self._make_one(client, BUCKET_NAME)
bucket.create(project=OTHER_PROJECT)
connection.api_request.assert_called_once_with(
method="POST",
path="/b",
query_params={"project": OTHER_PROJECT},
data=DATA,
_target_object=bucket
)

def test_create_w_explicit_location(self):
from google.cloud.storage.client import Client

PROJECT = "PROJECT"
BUCKET_NAME = "bucket-name"
LOCATION = "us-central1"
DATA = {"location": LOCATION, "name": BUCKET_NAME}
connection = _Connection(
DATA, "{'location': 'us-central1', 'name': 'bucket-name'}"

connection = _make_connection(
DATA,
"{'location': 'us-central1', 'name': 'bucket-name'}"
)
client = _Client(connection, project=PROJECT)
bucket = self._make_one(client, BUCKET_NAME)

bucket.create(location=LOCATION)
client = Client(project=PROJECT)
with mock.patch(
'google.cloud.storage.client.Client._connection',
new_callable=mock.PropertyMock
) as client_mock:
client_mock.return_value = connection

kw, = connection._requested
self.assertEqual(kw["method"], "POST")
self.assertEqual(kw["path"], "/b")
self.assertEqual(kw["data"], DATA)
self.assertEqual(bucket.location, LOCATION)
bucket = self._make_one(client, BUCKET_NAME)

bucket.create(location=LOCATION)

connection.api_request.assert_called_once_with(
method="POST",
path="/b",
data=DATA,
_target_object=bucket,
query_params={'project': 'PROJECT'}
)
self.assertEqual(bucket.location, LOCATION)

def test_create_hit(self):
from google.cloud.storage.client import Client

PROJECT = "PROJECT"
BUCKET_NAME = "bucket-name"
DATA = {"name": BUCKET_NAME}
connection = _Connection(DATA)
client = _Client(connection, project=PROJECT)
bucket = self._make_one(client=client, name=BUCKET_NAME)
bucket.create()
connection = _make_connection(DATA)
client = Client(project=PROJECT)
with mock.patch(
'google.cloud.storage.client.Client._connection',
new_callable=mock.PropertyMock
) as client_mock:
client_mock.return_value = connection

bucket = self._make_one(client=client, name=BUCKET_NAME)
bucket.create()

kw, = connection._requested
self.assertEqual(kw["method"], "POST")
self.assertEqual(kw["path"], "/b")
self.assertEqual(kw["query_params"], {"project": PROJECT})
self.assertEqual(kw["data"], DATA)
connection.api_request.assert_called_once_with(
method="POST",
path="/b",
query_params={"project": PROJECT},
data=DATA,
_target_object=bucket
)

def test_create_w_extra_properties(self):
from google.cloud.storage.client import Client

BUCKET_NAME = "bucket-name"
PROJECT = "PROJECT"
CORS = [
Expand All @@ -619,22 +671,31 @@ def test_create_w_extra_properties(self):
"billing": {"requesterPays": True},
"labels": LABELS,
}
connection = _Connection(DATA)
client = _Client(connection, project=PROJECT)
bucket = self._make_one(client=client, name=BUCKET_NAME)
bucket.cors = CORS
bucket.lifecycle_rules = LIFECYCLE_RULES
bucket.storage_class = STORAGE_CLASS
bucket.versioning_enabled = True
bucket.requester_pays = True
bucket.labels = LABELS
bucket.create(location=LOCATION)

kw, = connection._requested
self.assertEqual(kw["method"], "POST")
self.assertEqual(kw["path"], "/b")
self.assertEqual(kw["query_params"], {"project": PROJECT})
self.assertEqual(kw["data"], DATA)
connection = _make_connection(DATA)
client = Client(project=PROJECT)
with mock.patch(
'google.cloud.storage.client.Client._connection',
new_callable=mock.PropertyMock
) as client_mock:
client_mock.return_value = connection

bucket = self._make_one(client=client, name=BUCKET_NAME)
bucket.cors = CORS
bucket.lifecycle_rules = LIFECYCLE_RULES
bucket.storage_class = STORAGE_CLASS
bucket.versioning_enabled = True
bucket.requester_pays = True
bucket.labels = LABELS
bucket.create(location=LOCATION)

connection.api_request.assert_called_once_with(
method="POST",
path="/b",
query_params={"project": PROJECT},
data=DATA,
_target_object=bucket
)

def test_acl_property(self):
from google.cloud.storage.acl import BucketACL
Expand Down