Skip to content

Commit 41b6b98

Browse files
tseaverlandrito
authored andcommitted
Pass 'user_project' if set for BucketACL/BlobACL API requests (googleapis#3499)
1 parent a80206e commit 41b6b98

File tree

2 files changed

+124
-36
lines changed

2 files changed

+124
-36
lines changed

storage/google/cloud/storage/acl.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ class ACL(object):
198198
# as properties).
199199
reload_path = None
200200
save_path = None
201+
user_project = None
201202

202203
def __init__(self):
203204
self.entities = {}
@@ -405,10 +406,18 @@ def reload(self, client=None):
405406
"""
406407
path = self.reload_path
407408
client = self._require_client(client)
409+
query_params = {}
410+
411+
if self.user_project is not None:
412+
query_params['userProject'] = self.user_project
408413

409414
self.entities.clear()
410415

411-
found = client._connection.api_request(method='GET', path=path)
416+
found = client._connection.api_request(
417+
method='GET',
418+
path=path,
419+
query_params=query_params,
420+
)
412421
self.loaded = True
413422
for entry in found.get('items', ()):
414423
self.add_entity(self.entity_from_dict(entry))
@@ -435,8 +444,12 @@ def _save(self, acl, predefined, client):
435444
acl = []
436445
query_params[self._PREDEFINED_QUERY_PARAM] = predefined
437446

447+
if self.user_project is not None:
448+
query_params['userProject'] = self.user_project
449+
438450
path = self.save_path
439451
client = self._require_client(client)
452+
440453
result = client._connection.api_request(
441454
method='PATCH',
442455
path=path,
@@ -532,6 +545,11 @@ def save_path(self):
532545
"""Compute the path for PATCH API requests for this ACL."""
533546
return self.bucket.path
534547

548+
@property
549+
def user_project(self):
550+
"""Compute the user project charged for API requests for this ACL."""
551+
return self.bucket.user_project
552+
535553

536554
class DefaultObjectACL(BucketACL):
537555
"""A class representing the default object ACL for a bucket."""
@@ -565,3 +583,8 @@ def reload_path(self):
565583
def save_path(self):
566584
"""Compute the path for PATCH API requests for this ACL."""
567585
return self.blob.path
586+
587+
@property
588+
def user_project(self):
589+
"""Compute the user project charged for API requests for this ACL."""
590+
return self.blob.user_project

storage/tests/unit/test_acl.py

Lines changed: 100 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,11 @@ def test_reload_missing(self):
532532
self.assertEqual(list(acl), [])
533533
kw = connection._requested
534534
self.assertEqual(len(kw), 1)
535-
self.assertEqual(kw[0]['method'], 'GET')
536-
self.assertEqual(kw[0]['path'], '/testing/acl')
535+
self.assertEqual(kw[0], {
536+
'method': 'GET',
537+
'path': '/testing/acl',
538+
'query_params': {},
539+
})
537540

538541
def test_reload_empty_result_clears_local(self):
539542
ROLE = 'role'
@@ -543,29 +546,41 @@ def test_reload_empty_result_clears_local(self):
543546
acl.reload_path = '/testing/acl'
544547
acl.loaded = True
545548
acl.entity('allUsers', ROLE)
549+
546550
acl.reload(client=client)
551+
547552
self.assertTrue(acl.loaded)
548553
self.assertEqual(list(acl), [])
549554
kw = connection._requested
550555
self.assertEqual(len(kw), 1)
551-
self.assertEqual(kw[0]['method'], 'GET')
552-
self.assertEqual(kw[0]['path'], '/testing/acl')
556+
self.assertEqual(kw[0], {
557+
'method': 'GET',
558+
'path': '/testing/acl',
559+
'query_params': {},
560+
})
553561

554-
def test_reload_nonempty_result(self):
562+
def test_reload_nonempty_result_w_user_project(self):
555563
ROLE = 'role'
564+
USER_PROJECT = 'user-project-123'
556565
connection = _Connection(
557566
{'items': [{'entity': 'allUsers', 'role': ROLE}]})
558567
client = _Client(connection)
559568
acl = self._make_one()
560569
acl.reload_path = '/testing/acl'
561570
acl.loaded = True
571+
acl.user_project = USER_PROJECT
572+
562573
acl.reload(client=client)
574+
563575
self.assertTrue(acl.loaded)
564576
self.assertEqual(list(acl), [{'entity': 'allUsers', 'role': ROLE}])
565577
kw = connection._requested
566578
self.assertEqual(len(kw), 1)
567-
self.assertEqual(kw[0]['method'], 'GET')
568-
self.assertEqual(kw[0]['path'], '/testing/acl')
579+
self.assertEqual(kw[0], {
580+
'method': 'GET',
581+
'path': '/testing/acl',
582+
'query_params': {'userProject': USER_PROJECT},
583+
})
569584

570585
def test_save_none_set_none_passed(self):
571586
connection = _Connection()
@@ -606,30 +621,43 @@ def test_save_no_acl(self):
606621
self.assertEqual(len(kw), 1)
607622
self.assertEqual(kw[0]['method'], 'PATCH')
608623
self.assertEqual(kw[0]['path'], '/testing')
609-
self.assertEqual(kw[0]['data'], {'acl': AFTER})
610-
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
611-
612-
def test_save_w_acl(self):
624+
self.assertEqual(kw[0], {
625+
'method': 'PATCH',
626+
'path': '/testing',
627+
'query_params': {'projection': 'full'},
628+
'data': {'acl': AFTER},
629+
})
630+
631+
def test_save_w_acl_w_user_project(self):
613632
ROLE1 = 'role1'
614633
ROLE2 = 'role2'
615634
STICKY = {'entity': 'allUsers', 'role': ROLE2}
635+
USER_PROJECT = 'user-project-123'
616636
new_acl = [{'entity': 'allUsers', 'role': ROLE1}]
617637
connection = _Connection({'acl': [STICKY] + new_acl})
618638
client = _Client(connection)
619639
acl = self._make_one()
620640
acl.save_path = '/testing'
621641
acl.loaded = True
642+
acl.user_project = USER_PROJECT
643+
622644
acl.save(new_acl, client=client)
645+
623646
entries = list(acl)
624647
self.assertEqual(len(entries), 2)
625648
self.assertTrue(STICKY in entries)
626649
self.assertTrue(new_acl[0] in entries)
627650
kw = connection._requested
628651
self.assertEqual(len(kw), 1)
629-
self.assertEqual(kw[0]['method'], 'PATCH')
630-
self.assertEqual(kw[0]['path'], '/testing')
631-
self.assertEqual(kw[0]['data'], {'acl': new_acl})
632-
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
652+
self.assertEqual(kw[0], {
653+
'method': 'PATCH',
654+
'path': '/testing',
655+
'query_params': {
656+
'projection': 'full',
657+
'userProject': USER_PROJECT,
658+
},
659+
'data': {'acl': new_acl},
660+
})
633661

634662
def test_save_prefefined_invalid(self):
635663
connection = _Connection()
@@ -652,11 +680,15 @@ def test_save_predefined_valid(self):
652680
self.assertEqual(len(entries), 0)
653681
kw = connection._requested
654682
self.assertEqual(len(kw), 1)
655-
self.assertEqual(kw[0]['method'], 'PATCH')
656-
self.assertEqual(kw[0]['path'], '/testing')
657-
self.assertEqual(kw[0]['data'], {'acl': []})
658-
self.assertEqual(kw[0]['query_params'],
659-
{'projection': 'full', 'predefinedAcl': PREDEFINED})
683+
self.assertEqual(kw[0], {
684+
'method': 'PATCH',
685+
'path': '/testing',
686+
'query_params': {
687+
'projection': 'full',
688+
'predefinedAcl': PREDEFINED,
689+
},
690+
'data': {'acl': []},
691+
})
660692

661693
def test_save_predefined_w_XML_alias(self):
662694
PREDEFINED_XML = 'project-private'
@@ -671,12 +703,15 @@ def test_save_predefined_w_XML_alias(self):
671703
self.assertEqual(len(entries), 0)
672704
kw = connection._requested
673705
self.assertEqual(len(kw), 1)
674-
self.assertEqual(kw[0]['method'], 'PATCH')
675-
self.assertEqual(kw[0]['path'], '/testing')
676-
self.assertEqual(kw[0]['data'], {'acl': []})
677-
self.assertEqual(kw[0]['query_params'],
678-
{'projection': 'full',
679-
'predefinedAcl': PREDEFINED_JSON})
706+
self.assertEqual(kw[0], {
707+
'method': 'PATCH',
708+
'path': '/testing',
709+
'query_params': {
710+
'projection': 'full',
711+
'predefinedAcl': PREDEFINED_JSON,
712+
},
713+
'data': {'acl': []},
714+
})
680715

681716
def test_save_predefined_valid_w_alternate_query_param(self):
682717
# Cover case where subclass overrides _PREDEFINED_QUERY_PARAM
@@ -692,11 +727,15 @@ def test_save_predefined_valid_w_alternate_query_param(self):
692727
self.assertEqual(len(entries), 0)
693728
kw = connection._requested
694729
self.assertEqual(len(kw), 1)
695-
self.assertEqual(kw[0]['method'], 'PATCH')
696-
self.assertEqual(kw[0]['path'], '/testing')
697-
self.assertEqual(kw[0]['data'], {'acl': []})
698-
self.assertEqual(kw[0]['query_params'],
699-
{'projection': 'full', 'alternate': PREDEFINED})
730+
self.assertEqual(kw[0], {
731+
'method': 'PATCH',
732+
'path': '/testing',
733+
'query_params': {
734+
'projection': 'full',
735+
'alternate': PREDEFINED,
736+
},
737+
'data': {'acl': []},
738+
})
700739

701740
def test_clear(self):
702741
ROLE1 = 'role1'
@@ -712,10 +751,12 @@ def test_clear(self):
712751
self.assertEqual(list(acl), [STICKY])
713752
kw = connection._requested
714753
self.assertEqual(len(kw), 1)
715-
self.assertEqual(kw[0]['method'], 'PATCH')
716-
self.assertEqual(kw[0]['path'], '/testing')
717-
self.assertEqual(kw[0]['data'], {'acl': []})
718-
self.assertEqual(kw[0]['query_params'], {'projection': 'full'})
754+
self.assertEqual(kw[0], {
755+
'method': 'PATCH',
756+
'path': '/testing',
757+
'query_params': {'projection': 'full'},
758+
'data': {'acl': []},
759+
})
719760

720761

721762
class Test_BucketACL(unittest.TestCase):
@@ -739,6 +780,15 @@ def test_ctor(self):
739780
self.assertEqual(acl.reload_path, '/b/%s/acl' % NAME)
740781
self.assertEqual(acl.save_path, '/b/%s' % NAME)
741782

783+
def test_user_project(self):
784+
NAME = 'name'
785+
USER_PROJECT = 'user-project-123'
786+
bucket = _Bucket(NAME)
787+
acl = self._make_one(bucket)
788+
self.assertIsNone(acl.user_project)
789+
bucket.user_project = USER_PROJECT
790+
self.assertEqual(acl.user_project, USER_PROJECT)
791+
742792

743793
class Test_DefaultObjectACL(unittest.TestCase):
744794

@@ -785,9 +835,22 @@ def test_ctor(self):
785835
self.assertEqual(acl.reload_path, '/b/%s/o/%s/acl' % (NAME, BLOB_NAME))
786836
self.assertEqual(acl.save_path, '/b/%s/o/%s' % (NAME, BLOB_NAME))
787837

838+
def test_user_project(self):
839+
NAME = 'name'
840+
BLOB_NAME = 'blob-name'
841+
USER_PROJECT = 'user-project-123'
842+
bucket = _Bucket(NAME)
843+
blob = _Blob(bucket, BLOB_NAME)
844+
acl = self._make_one(blob)
845+
self.assertIsNone(acl.user_project)
846+
blob.user_project = USER_PROJECT
847+
self.assertEqual(acl.user_project, USER_PROJECT)
848+
788849

789850
class _Blob(object):
790851

852+
user_project = None
853+
791854
def __init__(self, bucket, blob):
792855
self.bucket = bucket
793856
self.blob = blob
@@ -799,6 +862,8 @@ def path(self):
799862

800863
class _Bucket(object):
801864

865+
user_project = None
866+
802867
def __init__(self, name):
803868
self.name = name
804869

0 commit comments

Comments
 (0)