diff --git a/storage/google/cloud/storage/_helpers.py b/storage/google/cloud/storage/_helpers.py index 5322f8a615b7..3a4eb2e232a2 100644 --- a/storage/google/cloud/storage/_helpers.py +++ b/storage/google/cloud/storage/_helpers.py @@ -67,11 +67,6 @@ def client(self): """Abstract getter for the object client.""" raise NotImplementedError - @property - def user_project(self): - """Abstract getter for the object user_project.""" - raise NotImplementedError - def _require_client(self, client): """Check client or verify over-ride. @@ -99,8 +94,6 @@ def reload(self, client=None): # Pass only '?projection=noAcl' here because 'acl' and related # are handled via custom endpoints. query_params = {'projection': 'noAcl'} - if self.user_project is not None: - query_params['userProject'] = self.user_project api_response = client._connection.api_request( method='GET', path=self.path, query_params=query_params, _target_object=self) @@ -147,16 +140,13 @@ def patch(self, client=None): client = self._require_client(client) # Pass '?projection=full' here because 'PATCH' documented not # to work properly w/ 'noAcl'. - query_params = {'projection': 'full'} - if self.user_project is not None: - query_params['userProject'] = self.user_project update_properties = {key: self._properties[key] for key in self._changes} # Make the API call. api_response = client._connection.api_request( method='PATCH', path=self.path, data=update_properties, - query_params=query_params, _target_object=self) + query_params={'projection': 'full'}, _target_object=self) self._set_properties(api_response) def update(self, client=None): @@ -170,12 +160,9 @@ def update(self, client=None): ``client`` stored on the current object. """ client = self._require_client(client) - query_params = {'projection': 'full'} - if self.user_project is not None: - query_params['userProject'] = self.user_project api_response = client._connection.api_request( method='PUT', path=self.path, data=self._properties, - query_params=query_params, _target_object=self) + query_params={'projection': 'full'}, _target_object=self) self._set_properties(api_response) diff --git a/storage/google/cloud/storage/acl.py b/storage/google/cloud/storage/acl.py index 240662c4dc8d..c4525ea88735 100644 --- a/storage/google/cloud/storage/acl.py +++ b/storage/google/cloud/storage/acl.py @@ -198,7 +198,6 @@ class ACL(object): # as properties). reload_path = None save_path = None - user_project = None def __init__(self): self.entities = {} @@ -406,18 +405,10 @@ def reload(self, client=None): """ path = self.reload_path client = self._require_client(client) - query_params = {} - - if self.user_project is not None: - query_params['userProject'] = self.user_project self.entities.clear() - found = client._connection.api_request( - method='GET', - path=path, - query_params=query_params, - ) + found = client._connection.api_request(method='GET', path=path) self.loaded = True for entry in found.get('items', ()): self.add_entity(self.entity_from_dict(entry)) @@ -444,12 +435,8 @@ def _save(self, acl, predefined, client): acl = [] query_params[self._PREDEFINED_QUERY_PARAM] = predefined - if self.user_project is not None: - query_params['userProject'] = self.user_project - path = self.save_path client = self._require_client(client) - result = client._connection.api_request( method='PATCH', path=path, @@ -545,11 +532,6 @@ def save_path(self): """Compute the path for PATCH API requests for this ACL.""" return self.bucket.path - @property - def user_project(self): - """Compute the user project charged for API requests for this ACL.""" - return self.bucket.user_project - class DefaultObjectACL(BucketACL): """A class representing the default object ACL for a bucket.""" @@ -583,8 +565,3 @@ def reload_path(self): def save_path(self): """Compute the path for PATCH API requests for this ACL.""" return self.blob.path - - @property - def user_project(self): - """Compute the user project charged for API requests for this ACL.""" - return self.blob.user_project diff --git a/storage/google/cloud/storage/blob.py b/storage/google/cloud/storage/blob.py index a9d28dda9e75..3bf5228b997a 100644 --- a/storage/google/cloud/storage/blob.py +++ b/storage/google/cloud/storage/blob.py @@ -34,11 +34,7 @@ import time import warnings -from six.moves.urllib.parse import parse_qsl from six.moves.urllib.parse import quote -from six.moves.urllib.parse import urlencode -from six.moves.urllib.parse import urlsplit -from six.moves.urllib.parse import urlunsplit from google import resumable_media from google.resumable_media.requests import ChunkedDownload @@ -224,16 +220,6 @@ def client(self): """The client bound to this blob.""" return self.bucket.client - @property - def user_project(self): - """Project ID used for API requests made via this blob. - - Derived from bucket's value. - - :rtype: str - """ - return self.bucket.user_project - @property def public_url(self): """The public URL for this blob's object. @@ -342,14 +328,10 @@ def exists(self, client=None): :returns: True if the blob exists in Cloud Storage. """ client = self._require_client(client) - # We only need the status code (200 or not) so we seek to - # minimize the returned payload. - query_params = {'fields': 'name'} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - try: + # We only need the status code (200 or not) so we seek to + # minimize the returned payload. + query_params = {'fields': 'name'} # We intentionally pass `_target_object=None` since fields=name # would limit the local properties. client._connection.api_request( @@ -403,19 +385,13 @@ def _get_download_url(self): :rtype: str :returns: The download URL for the current blob. """ - name_value_pairs = [] if self.media_link is None: - base_url = _DOWNLOAD_URL_TEMPLATE.format(path=self.path) + download_url = _DOWNLOAD_URL_TEMPLATE.format(path=self.path) if self.generation is not None: - name_value_pairs.append( - ('generation', '{:d}'.format(self.generation))) + download_url += u'&generation={:d}'.format(self.generation) + return download_url else: - base_url = self.media_link - - if self.user_project is not None: - name_value_pairs.append(('userProject', self.user_project)) - - return _add_query_parameters(base_url, name_value_pairs) + return self.media_link def _do_download(self, transport, file_obj, download_url, headers): """Perform a download without any error handling. @@ -482,9 +458,8 @@ def download_to_file(self, file_obj, client=None): """ download_url = self._get_download_url() headers = _get_encryption_headers(self._encryption_key) - headers['accept-encoding'] = 'gzip' - transport = self._get_transport(client) + try: self._do_download(transport, file_obj, download_url, headers) except resumable_media.InvalidResponse as exc: @@ -667,14 +642,8 @@ def _do_multipart_upload(self, client, stream, content_type, info = self._get_upload_arguments(content_type) headers, object_metadata, content_type = info - base_url = _MULTIPART_URL_TEMPLATE.format( + upload_url = _MULTIPART_URL_TEMPLATE.format( bucket_path=self.bucket.path) - name_value_pairs = [] - - if self.user_project is not None: - name_value_pairs.append(('userProject', self.user_project)) - - upload_url = _add_query_parameters(base_url, name_value_pairs) upload = MultipartUpload(upload_url, headers=headers) if num_retries is not None: @@ -745,14 +714,8 @@ def _initiate_resumable_upload(self, client, stream, content_type, if extra_headers is not None: headers.update(extra_headers) - base_url = _RESUMABLE_URL_TEMPLATE.format( + upload_url = _RESUMABLE_URL_TEMPLATE.format( bucket_path=self.bucket.path) - name_value_pairs = [] - - if self.user_project is not None: - name_value_pairs.append(('userProject', self.user_project)) - - upload_url = _add_query_parameters(base_url, name_value_pairs) upload = ResumableUpload(upload_url, chunk_size, headers=headers) if num_retries is not None: @@ -1106,16 +1069,9 @@ def get_iam_policy(self, client=None): the ``getIamPolicy`` API request. """ client = self._require_client(client) - - query_params = {} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - info = client._connection.api_request( method='GET', path='%s/iam' % (self.path,), - query_params=query_params, _target_object=None) return Policy.from_api_repr(info) @@ -1138,18 +1094,11 @@ def set_iam_policy(self, policy, client=None): the ``setIamPolicy`` API request. """ client = self._require_client(client) - - query_params = {} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - resource = policy.to_api_repr() resource['resourceId'] = self.path info = client._connection.api_request( method='PUT', path='%s/iam' % (self.path,), - query_params=query_params, data=resource, _target_object=None) return Policy.from_api_repr(info) @@ -1173,17 +1122,12 @@ def test_iam_permissions(self, permissions, client=None): request. """ client = self._require_client(client) - query_params = {'permissions': permissions} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - + query = {'permissions': permissions} path = '%s/iam/testPermissions' % (self.path,) resp = client._connection.api_request( method='GET', path=path, - query_params=query_params) - + query_params=query) return resp.get('permissions', []) def make_public(self, client=None): @@ -1213,22 +1157,13 @@ def compose(self, sources, client=None): """ if self.content_type is None: raise ValueError("Destination 'content_type' not set.") - client = self._require_client(client) - query_params = {} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - request = { 'sourceObjects': [{'name': source.name} for source in sources], 'destination': self._properties.copy(), } api_response = client._connection.api_request( - method='POST', - path=self.path + '/compose', - query_params=query_params, - data=request, + method='POST', path=self.path + '/compose', data=request, _target_object=self) self._set_properties(api_response) @@ -1260,20 +1195,14 @@ def rewrite(self, source, token=None, client=None): headers.update(_get_encryption_headers( source._encryption_key, source=True)) - query_params = {} - if token: - query_params['rewriteToken'] = token - - if self.user_project is not None: - query_params['userProject'] = self.user_project + query_params = {'rewriteToken': token} + else: + query_params = {} api_response = client._connection.api_request( - method='POST', - path=source.path + '/rewriteTo' + self.path, - query_params=query_params, - data=self._properties, - headers=headers, + method='POST', path=source.path + '/rewriteTo' + self.path, + query_params=query_params, data=self._properties, headers=headers, _target_object=self) rewritten = int(api_response['totalBytesRewritten']) size = int(api_response['objectSize']) @@ -1304,22 +1233,13 @@ def update_storage_class(self, new_class, client=None): raise ValueError("Invalid storage class: %s" % (new_class,)) client = self._require_client(client) - - query_params = {} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - headers = _get_encryption_headers(self._encryption_key) headers.update(_get_encryption_headers( self._encryption_key, source=True)) api_response = client._connection.api_request( - method='POST', - path=self.path + '/rewriteTo' + self.path, - query_params=query_params, - data={'storageClass': new_class}, - headers=headers, + method='POST', path=self.path + '/rewriteTo' + self.path, + data={'storageClass': new_class}, headers=headers, _target_object=self) self._set_properties(api_response['resource']) @@ -1688,24 +1608,3 @@ def _raise_from_invalid_response(error): to the failed status code """ raise exceptions.from_http_response(error.response) - - -def _add_query_parameters(base_url, name_value_pairs): - """Add one query parameter to a base URL. - - :type base_url: string - :param base_url: Base URL (may already contain query parameters) - - :type name_value_pairs: list of (string, string) tuples. - :param name_value_pairs: Names and values of the query parameters to add - - :rtype: string - :returns: URL with additional query strings appended. - """ - if len(name_value_pairs) == 0: - return base_url - - scheme, netloc, path, query, frag = urlsplit(base_url) - query = parse_qsl(query) - query.extend(name_value_pairs) - return urlunsplit((scheme, netloc, path, urlencode(query), frag)) diff --git a/storage/google/cloud/storage/bucket.py b/storage/google/cloud/storage/bucket.py index f39bf8e1e7dd..88ac69566e46 100644 --- a/storage/google/cloud/storage/bucket.py +++ b/storage/google/cloud/storage/bucket.py @@ -35,8 +35,6 @@ from google.cloud.storage.acl import DefaultObjectACL from google.cloud.storage.blob import Blob from google.cloud.storage.blob import _get_encryption_headers -from google.cloud.storage.notification import BucketNotification -from google.cloud.storage.notification import NONE_PAYLOAD_FORMAT def _blobs_page_start(iterator, page, response): @@ -78,26 +76,6 @@ def _item_to_blob(iterator, item): return blob -def _item_to_notification(iterator, item): - """Convert a JSON blob to the native object. - - .. note:: - - This assumes that the ``bucket`` attribute has been - added to the iterator after being created. - - :type iterator: :class:`~google.api.core.page_iterator.Iterator` - :param iterator: The iterator that has retrieved the item. - - :type item: dict - :param item: An item to be converted to a blob. - - :rtype: :class:`.BucketNotification` - :returns: The next notification being iterated. - """ - return BucketNotification.from_api_repr(item, bucket=iterator.bucket) - - class Bucket(_PropertyMixin): """A class representing a Bucket on Cloud Storage. @@ -108,10 +86,6 @@ class Bucket(_PropertyMixin): :type name: str :param name: The name of the bucket. Bucket names must start and end with a number or letter. - - :type user_project: str - :param user_project: (Optional) the project ID to be billed for API - requests made via this instance. """ _MAX_OBJECTS_FOR_ITERATION = 256 @@ -135,14 +109,13 @@ class Bucket(_PropertyMixin): https://cloud.google.com/storage/docs/storage-classes """ - def __init__(self, client, name=None, user_project=None): + def __init__(self, client, name=None): name = _validate_name(name) super(Bucket, self).__init__(name=name) self._client = client self._acl = BucketACL(self) self._default_object_acl = DefaultObjectACL(self) self._label_removals = set() - self._user_project = user_project def __repr__(self): return '' % (self.name,) @@ -161,16 +134,6 @@ def _set_properties(self, value): self._label_removals.clear() return super(Bucket, self)._set_properties(value) - @property - def user_project(self): - """Project ID to be billed for API requests made via this bucket. - - If unset, API requests are billed to the bucket owner. - - :rtype: str - """ - return self._user_project - def blob(self, blob_name, chunk_size=None, encryption_key=None): """Factory constructor for blob object. @@ -196,27 +159,6 @@ def blob(self, blob_name, chunk_size=None, encryption_key=None): return Blob(name=blob_name, bucket=self, chunk_size=chunk_size, encryption_key=encryption_key) - def notification(self, topic_name, - topic_project=None, - custom_attributes=None, - event_types=None, - blob_name_prefix=None, - payload_format=NONE_PAYLOAD_FORMAT): - """Factory: create a notification resource for the bucket. - - See: :class:`.BucketNotification` for parameters. - - :rtype: :class:`.BucketNotification` - """ - return BucketNotification( - self, topic_name, - topic_project=topic_project, - custom_attributes=custom_attributes, - event_types=event_types, - blob_name_prefix=blob_name_prefix, - payload_format=payload_format, - ) - def exists(self, client=None): """Determines whether or not this bucket exists. @@ -229,14 +171,10 @@ def exists(self, client=None): :returns: True if the bucket exists in Cloud Storage. """ client = self._require_client(client) - # We only need the status code (200 or not) so we seek to - # minimize the returned payload. - query_params = {'fields': 'name'} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - try: + # We only need the status code (200 or not) so we seek to + # minimize the returned payload. + query_params = {'fields': 'name'} # We intentionally pass `_target_object=None` since fields=name # would limit the local properties. client._connection.api_request( @@ -262,9 +200,6 @@ def create(self, client=None): :param client: Optional. The client to use. If not passed, falls back to the ``client`` stored on the current bucket. """ - if self.user_project is not None: - raise ValueError("Cannot create bucket with 'user_project' set.") - client = self._require_client(client) query_params = {'project': client.project} properties = {key: self._properties[key] for key in self._changes} @@ -356,21 +291,13 @@ def get_blob(self, blob_name, client=None, encryption_key=None, **kwargs): :returns: The blob object if it exists, otherwise None. """ client = self._require_client(client) - query_params = {} - - if self.user_project is not None: - query_params['userProject'] = self.user_project blob = Blob(bucket=self, name=blob_name, encryption_key=encryption_key, **kwargs) try: headers = _get_encryption_headers(encryption_key) response = client._connection.api_request( - method='GET', - path=blob.path, - query_params=query_params, - headers=headers, - _target_object=blob, - ) + method='GET', path=blob.path, _target_object=blob, + headers=headers) # NOTE: We assume response.get('name') matches `blob_name`. blob._set_properties(response) # NOTE: This will not fail immediately in a batch. However, when @@ -424,7 +351,7 @@ def list_blobs(self, max_results=None, page_token=None, prefix=None, :returns: Iterator of all :class:`~google.cloud.storage.blob.Blob` in this bucket matching the arguments. """ - extra_params = {'projection': projection} + extra_params = {} if prefix is not None: extra_params['prefix'] = prefix @@ -435,12 +362,11 @@ def list_blobs(self, max_results=None, page_token=None, prefix=None, if versions is not None: extra_params['versions'] = versions + extra_params['projection'] = projection + if fields is not None: extra_params['fields'] = fields - if self.user_project is not None: - extra_params['userProject'] = self.user_project - client = self._require_client(client) path = self.path + '/o' iterator = page_iterator.HTTPIterator( @@ -456,30 +382,6 @@ def list_blobs(self, max_results=None, page_token=None, prefix=None, iterator.prefixes = set() return iterator - def list_notifications(self, client=None): - """List Pub / Sub notifications for this bucket. - - See: - https://cloud.google.com/storage/docs/json_api/v1/notifications/list - - :type client: :class:`~google.cloud.storage.client.Client` or - ``NoneType`` - :param client: Optional. The client to use. If not passed, falls back - to the ``client`` stored on the current bucket. - - :rtype: list of :class:`.BucketNotification` - :returns: notification instances - """ - client = self._require_client(client) - path = self.path + '/notificationConfigs' - iterator = page_iterator.HTTPIterator( - client=client, - api_request=client._connection.api_request, - path=path, - item_to_value=_item_to_notification) - iterator.bucket = self - return iterator - def delete(self, force=False, client=None): """Delete this bucket. @@ -509,11 +411,6 @@ def delete(self, force=False, client=None): contains more than 256 objects / blobs. """ client = self._require_client(client) - query_params = {} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - if force: blobs = list(self.list_blobs( max_results=self._MAX_OBJECTS_FOR_ITERATION + 1, @@ -535,10 +432,7 @@ def delete(self, force=False, client=None): # request has no response value (whether in a standard request or # in a batch request). client._connection.api_request( - method='DELETE', - path=self.path, - query_params=query_params, - _target_object=None) + method='DELETE', path=self.path, _target_object=None) def delete_blob(self, blob_name, client=None): """Deletes a blob from the current bucket. @@ -570,20 +464,12 @@ def delete_blob(self, blob_name, client=None): """ client = self._require_client(client) - query_params = {} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - blob_path = Blob.path_helper(self.path, blob_name) # We intentionally pass `_target_object=None` since a DELETE # request has no response value (whether in a standard request or # in a batch request). client._connection.api_request( - method='DELETE', - path=blob_path, - query_params=query_params, - _target_object=None) + method='DELETE', path=blob_path, _target_object=None) def delete_blobs(self, blobs, on_error=None, client=None): """Deletes a list of blobs from the current bucket. @@ -646,26 +532,14 @@ def copy_blob(self, blob, destination_bucket, new_name=None, :returns: The new Blob. """ client = self._require_client(client) - query_params = {} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - if new_name is None: new_name = blob.name - new_blob = Blob(bucket=destination_bucket, name=new_name) api_path = blob.path + '/copyTo' + new_blob.path copy_result = client._connection.api_request( - method='POST', - path=api_path, - query_params=query_params, - _target_object=new_blob, - ) - + method='POST', path=api_path, _target_object=new_blob) if not preserve_acl: new_blob.acl.save(acl={}, client=client) - new_blob._set_properties(copy_result) return new_blob @@ -1023,40 +897,10 @@ def versioning_enabled(self, value): details. :type value: convertible to boolean - :param value: should versioning be enabled for the bucket? + :param value: should versioning be anabled for the bucket? """ self._patch_property('versioning', {'enabled': bool(value)}) - @property - def requester_pays(self): - """Does the requester pay for API requests for this bucket? - - .. note:: - - No public docs exist yet for the "requester pays" feature. - - :setter: Update whether requester pays for this bucket. - :getter: Query whether requester pays for this bucket. - - :rtype: bool - :returns: True if requester pays for API requests for the bucket, - else False. - """ - versioning = self._properties.get('billing', {}) - return versioning.get('requesterPays', False) - - @requester_pays.setter - def requester_pays(self, value): - """Update whether requester pays for API requests for this bucket. - - See https://cloud.google.com/storage/docs/ for - details. - - :type value: convertible to boolean - :param value: should requester pay for API requests for the bucket? - """ - self._patch_property('billing', {'requesterPays': bool(value)}) - def configure_website(self, main_page_suffix=None, not_found_page=None): """Configure website-related properties. @@ -1122,15 +966,9 @@ def get_iam_policy(self, client=None): the ``getIamPolicy`` API request. """ client = self._require_client(client) - query_params = {} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - info = client._connection.api_request( method='GET', path='%s/iam' % (self.path,), - query_params=query_params, _target_object=None) return Policy.from_api_repr(info) @@ -1153,17 +991,11 @@ def set_iam_policy(self, policy, client=None): the ``setIamPolicy`` API request. """ client = self._require_client(client) - query_params = {} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - resource = policy.to_api_repr() resource['resourceId'] = self.path info = client._connection.api_request( method='PUT', path='%s/iam' % (self.path,), - query_params=query_params, data=resource, _target_object=None) return Policy.from_api_repr(info) @@ -1187,16 +1019,12 @@ def test_iam_permissions(self, permissions, client=None): request. """ client = self._require_client(client) - query_params = {'permissions': permissions} - - if self.user_project is not None: - query_params['userProject'] = self.user_project - + query = {'permissions': permissions} path = '%s/iam/testPermissions' % (self.path,) resp = client._connection.api_request( method='GET', path=path, - query_params=query_params) + query_params=query) return resp.get('permissions', []) def make_public(self, recursive=False, future=False, client=None): diff --git a/storage/google/cloud/storage/client.py b/storage/google/cloud/storage/client.py index db66b8aa27c6..5743dc059936 100644 --- a/storage/google/cloud/storage/client.py +++ b/storage/google/cloud/storage/client.py @@ -121,7 +121,7 @@ def current_batch(self): """ return self._batch_stack.top - def bucket(self, bucket_name, user_project=None): + def bucket(self, bucket_name): """Factory constructor for bucket object. .. note:: @@ -131,14 +131,10 @@ def bucket(self, bucket_name, user_project=None): :type bucket_name: str :param bucket_name: The name of the bucket to be instantiated. - :type user_project: str - :param user_project: (Optional) the project ID to be billed for API - requests made via this instance. - :rtype: :class:`google.cloud.storage.bucket.Bucket` :returns: The bucket object created. """ - return Bucket(client=self, name=bucket_name, user_project=user_project) + return Bucket(client=self, name=bucket_name) def batch(self): """Factory constructor for batch object. @@ -198,7 +194,7 @@ def lookup_bucket(self, bucket_name): except NotFound: return None - def create_bucket(self, bucket_name, requester_pays=None): + def create_bucket(self, bucket_name): """Create a new bucket. For example: @@ -215,17 +211,10 @@ def create_bucket(self, bucket_name, requester_pays=None): :type bucket_name: str :param bucket_name: The bucket name to create. - :type requester_pays: bool - :param requester_pays: - (Optional) Whether requester pays for API requests for this - bucket and its blobs. - :rtype: :class:`google.cloud.storage.bucket.Bucket` :returns: The newly created bucket. """ bucket = Bucket(self, name=bucket_name) - if requester_pays is not None: - bucket.requester_pays = requester_pays bucket.create(client=self) return bucket diff --git a/storage/google/cloud/storage/notification.py b/storage/google/cloud/storage/notification.py deleted file mode 100644 index 89b1d3850d89..000000000000 --- a/storage/google/cloud/storage/notification.py +++ /dev/null @@ -1,345 +0,0 @@ -# Copyright 2017 Google Inc. -# -# 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. - -"""Support for bucket notification resources.""" - -import re - -from google.api.core.exceptions import NotFound - - -OBJECT_FINALIZE_EVENT_TYPE = 'OBJECT_FINALIZE' -OBJECT_METADATA_UPDATE_EVENT_TYPE = 'OBJECT_METADATA_UPDATE' -OBJECT_DELETE_EVENT_TYPE = 'OBJECT_DELETE' -OBJECT_ARCHIVE_EVENT_TYPE = 'OBJECT_ARCHIVE' - -JSON_API_V1_PAYLOAD_FORMAT = 'JSON_API_V1' -NONE_PAYLOAD_FORMAT = 'NONE' - -_TOPIC_REF_FMT = '//pubsub.googleapis.com/projects/{}/topics/{}' -_PROJECT_PATTERN = r'(?P[a-z]+-[a-z]+-\d+)' -_TOPIC_NAME_PATTERN = r'(?P[A-Za-z](\w|[-_.~+%])+)' -_TOPIC_REF_PATTERN = _TOPIC_REF_FMT.format( - _PROJECT_PATTERN, _TOPIC_NAME_PATTERN) -_TOPIC_REF_RE = re.compile(_TOPIC_REF_PATTERN) - - -class BucketNotification(object): - """Represent a single notification resource for a bucket. - - See: https://cloud.google.com/storage/docs/json_api/v1/notifications - - :type bucket: :class:`google.cloud.storage.bucket.Bucket` - :param bucket: Bucket to which the notification is bound. - - :type topic_name: str - :param topic_name: Topic name to which notifications are published. - - :type topic_project: str - :param topic_project: - (Optional) project ID of topic to which notifications are published. - If not passed, uses the project ID of the bucket's client. - - :type custom_attributes: dict - :param custom_attributes: - (Optional) additional attributes passed with notification events. - - :type event_types: list(str) - :param event_types: - (Optional) event types for which notificatin events are published. - - :type blob_name_prefix: str - :param blob_name_prefix: - (Optional) prefix of blob names for which notification events are - published.. - - :type payload_format: str - :param payload_format: - (Optional) format of payload for notification events. - """ - def __init__(self, bucket, topic_name, - topic_project=None, custom_attributes=None, event_types=None, - blob_name_prefix=None, payload_format=NONE_PAYLOAD_FORMAT): - self._bucket = bucket - self._topic_name = topic_name - - if topic_project is None: - topic_project = bucket.client.project - self._topic_project = topic_project - - self._properties = {} - - if custom_attributes is not None: - self._properties['custom_attributes'] = custom_attributes - - if event_types is not None: - self._properties['event_types'] = event_types - - if blob_name_prefix is not None: - self._properties['object_name_prefix'] = blob_name_prefix - - self._properties['payload_format'] = payload_format - - @classmethod - def from_api_repr(cls, resource, bucket): - """Construct an instance from the JSON repr returned by the server. - - See: https://cloud.google.com/storage/docs/json_api/v1/notifications - - :type resource: dict - :param resource: JSON repr of the notification - - :type bucket: :class:`google.cloud.storage.bucket.Bucket` - :param bucket: Bucket to which the notification is bound. - - :rtype: :class:`BucketNotification` - :returns: the new notification instance - :raises ValueError: - if resource is missing 'topic' key, or if it is not formatted - per the spec documented in - https://cloud.google.com/storage/docs/json_api/v1/notifications/insert#topic - """ - topic_path = resource.get('topic') - if topic_path is None: - raise ValueError('Resource has no topic') - - match = _TOPIC_REF_RE.match(topic_path) - if match is None: - raise ValueError( - 'Resource has invalid topic: {}; see {}'.format( - topic_path, - 'https://cloud.google.com/storage/docs/json_api/v1/' - 'notifications/insert#topic')) - - name = match.group('name') - project = match.group('project') - instance = cls(bucket, name, topic_project=project) - instance._properties = resource - - return instance - - @property - def bucket(self): - """Bucket to which the notification is bound.""" - return self._bucket - - @property - def topic_name(self): - """Topic name to which notifications are published.""" - return self._topic_name - - @property - def topic_project(self): - """Project ID of topic to which notifications are published. - """ - return self._topic_project - - @property - def custom_attributes(self): - """Custom attributes passed with notification events. - """ - return self._properties.get('custom_attributes') - - @property - def event_types(self): - """Event types for which notification events are published. - """ - return self._properties.get('event_types') - - @property - def blob_name_prefix(self): - """Prefix of blob names for which notification events are published. - """ - return self._properties.get('object_name_prefix') - - @property - def payload_format(self): - """Format of payload of notification events.""" - return self._properties.get('payload_format') - - @property - def notification_id(self): - """Server-set ID of notification resource.""" - return self._properties.get('id') - - @property - def etag(self): - """Server-set ETag of notification resource.""" - return self._properties.get('etag') - - @property - def self_link(self): - """Server-set ETag of notification resource.""" - return self._properties.get('selfLink') - - @property - def client(self): - """The client bound to this notfication.""" - return self.bucket.client - - @property - def path(self): - """The URL path for this notification.""" - return '/b/{}/notificationConfigs/{}'.format( - self.bucket.name, self.notification_id) - - def _require_client(self, client): - """Check client or verify over-ride. - - :type client: :class:`~google.cloud.storage.client.Client` or - ``NoneType`` - :param client: the client to use. - - :rtype: :class:`google.cloud.storage.client.Client` - :returns: The client passed in or the bucket's client. - """ - if client is None: - client = self.client - return client - - def _set_properties(self, response): - """Helper for :meth:`reload`. - - :type response: dict - :param response: resource mapping from server - """ - self._properties.clear() - self._properties.update(response) - - def create(self, client=None): - """API wrapper: create the notification. - - See: - https://cloud.google.com/storage/docs/json_api/v1/notifications/insert - - :type client: :class:`~google.cloud.storage.client.Client` - :param client: (Optional) the client to use. If not passed, falls back - to the ``client`` stored on the notification's bucket. - """ - if self.notification_id is not None: - raise ValueError("Notification already exists w/ id: {}".format( - self.notification_id)) - - client = self._require_client(client) - - query_params = {} - if self.bucket.user_project is not None: - query_params['userProject'] = self.bucket.user_project - - path = '/b/{}/notificationConfigs'.format(self.bucket.name) - properties = self._properties.copy() - properties['topic'] = _TOPIC_REF_FMT.format( - self.topic_project, self.topic_name) - self._properties = client._connection.api_request( - method='POST', - path=path, - query_params=query_params, - data=properties, - ) - - def exists(self, client=None): - """Test whether this notification exists. - - See: - https://cloud.google.com/storage/docs/json_api/v1/notifications/get - - :type client: :class:`~google.cloud.storage.client.Client` or - ``NoneType`` - :param client: Optional. The client to use. If not passed, falls back - to the ``client`` stored on the current bucket. - - :rtype: bool - :returns: True, if the notification exists, else False. - :raises ValueError: if the notification has no ID. - """ - if self.notification_id is None: - raise ValueError("Notification not intialized by server") - - client = self._require_client(client) - - query_params = {} - if self.bucket.user_project is not None: - query_params['userProject'] = self.bucket.user_project - - try: - client._connection.api_request( - method='GET', - path=self.path, - query_params=query_params, - ) - except NotFound: - return False - else: - return True - - def reload(self, client=None): - """Update this notification from the server configuration. - - See: - https://cloud.google.com/storage/docs/json_api/v1/notifications/get - - :type client: :class:`~google.cloud.storage.client.Client` or - ``NoneType`` - :param client: Optional. The client to use. If not passed, falls back - to the ``client`` stored on the current bucket. - - :rtype: bool - :returns: True, if the notification exists, else False. - :raises ValueError: if the notification has no ID. - """ - if self.notification_id is None: - raise ValueError("Notification not intialized by server") - - client = self._require_client(client) - - query_params = {} - if self.bucket.user_project is not None: - query_params['userProject'] = self.bucket.user_project - - response = client._connection.api_request( - method='GET', - path=self.path, - query_params=query_params, - ) - self._set_properties(response) - - def delete(self, client=None): - """Delete this notification. - - See: - https://cloud.google.com/storage/docs/json_api/v1/notifications/delete - - :type client: :class:`~google.cloud.storage.client.Client` or - ``NoneType`` - :param client: Optional. The client to use. If not passed, falls back - to the ``client`` stored on the current bucket. - - :raises: :class:`google.api.core.exceptions.NotFound`: - if the notification does not exist. - :raises ValueError: if the notification has no ID. - """ - if self.notification_id is None: - raise ValueError("Notification not intialized by server") - - client = self._require_client(client) - - query_params = {} - if self.bucket.user_project is not None: - query_params['userProject'] = self.bucket.user_project - - client._connection.api_request( - method='DELETE', - path=self.path, - query_params=query_params, - ) diff --git a/storage/nox.py b/storage/nox.py index 9af298d88382..18ccf81aaff2 100644 --- a/storage/nox.py +++ b/storage/nox.py @@ -68,14 +68,13 @@ def system_tests(session, python_version): session.virtualenv_dirname = 'sys-' + python_version # Install all test dependencies, then install this package into the - # virtualenv's dist-packages. + # virutalenv's dist-packages. session.install('mock', 'pytest', *LOCAL_DEPS) session.install('../test_utils/') - session.install('../pubsub') - session.install('-e', '.') + session.install('.') # Run py.test against the system tests. - session.run('py.test', '--quiet', 'tests/system.py', *session.posargs) + session.run('py.test', '--quiet', 'tests/system.py') @nox.session diff --git a/storage/tests/system.py b/storage/tests/system.py index 2830fc33ebfb..e51cfcaeccb2 100644 --- a/storage/tests/system.py +++ b/storage/tests/system.py @@ -28,9 +28,6 @@ from test_utils.system import unique_resource_id -USER_PROJECT = os.environ.get('GOOGLE_CLOUD_TESTS_USER_PROJECT') - - def _bad_copy(bad_request): """Predicate: pass only exceptions for a failed copyTo.""" err_msg = bad_request.message @@ -86,9 +83,10 @@ def setUp(self): self.case_buckets_to_delete = [] def tearDown(self): - for bucket_name in self.case_buckets_to_delete: - bucket = Config.CLIENT.bucket(bucket_name) - retry_429(bucket.delete)() + with Config.CLIENT.batch(): + for bucket_name in self.case_buckets_to_delete: + bucket = Config.CLIENT.bucket(bucket_name) + retry_429(bucket.delete)() def test_create_bucket(self): new_bucket_name = 'a-new-bucket' + unique_resource_id('-') @@ -136,135 +134,6 @@ def test_bucket_update_labels(self): bucket.update() self.assertEqual(bucket.labels, {}) - @unittest.skipUnless(USER_PROJECT, 'USER_PROJECT not set in environment.') - def test_crud_bucket_with_requester_pays(self): - new_bucket_name = 'w-requester-pays' + unique_resource_id('-') - created = Config.CLIENT.create_bucket( - new_bucket_name, requester_pays=True) - self.case_buckets_to_delete.append(new_bucket_name) - self.assertEqual(created.name, new_bucket_name) - self.assertTrue(created.requester_pays) - - with_user_project = Config.CLIENT.bucket( - new_bucket_name, user_project=USER_PROJECT) - - # Bucket will be deleted in-line below. - self.case_buckets_to_delete.remove(new_bucket_name) - - try: - # Exercise 'buckets.get' w/ userProject. - self.assertTrue(with_user_project.exists()) - with_user_project.reload() - self.assertTrue(with_user_project.requester_pays) - - # Exercise 'buckets.patch' w/ userProject. - with_user_project.configure_website( - main_page_suffix='index.html', not_found_page='404.html') - with_user_project.patch() - self.assertEqual( - with_user_project._properties['website'], { - 'mainPageSuffix': 'index.html', - 'notFoundPage': '404.html', - }) - - # Exercise 'buckets.update' w/ userProject. - new_labels = {'another-label': 'another-value'} - with_user_project.labels = new_labels - with_user_project.update() - self.assertEqual(with_user_project.labels, new_labels) - - finally: - # Exercise 'buckets.delete' w/ userProject. - with_user_project.delete() - - @unittest.skipUnless(USER_PROJECT, 'USER_PROJECT not set in environment.') - def test_bucket_acls_iam_with_user_project(self): - new_bucket_name = 'acl-w-user-project' + unique_resource_id('-') - created = Config.CLIENT.create_bucket( - new_bucket_name, requester_pays=True) - self.case_buckets_to_delete.append(new_bucket_name) - - with_user_project = Config.CLIENT.bucket( - new_bucket_name, user_project=USER_PROJECT) - - # Exercise bucket ACL w/ userProject - acl = with_user_project.acl - acl.reload() - acl.all().grant_read() - acl.save() - self.assertIn('READER', acl.all().get_roles()) - del acl.entities['allUsers'] - acl.save() - self.assertFalse(acl.has_entity('allUsers')) - - # Exercise default object ACL w/ userProject - doa = with_user_project.default_object_acl - doa.reload() - doa.all().grant_read() - doa.save() - self.assertIn('READER', doa.all().get_roles()) - - # Exercise IAM w/ userProject - test_permissions = ['storage.buckets.get'] - self.assertEqual( - with_user_project.test_iam_permissions(test_permissions), - test_permissions) - - policy = with_user_project.get_iam_policy() - viewers = policy.setdefault('roles/storage.objectViewer', set()) - viewers.add(policy.all_users()) - with_user_project.set_iam_policy(policy) - - @unittest.skipUnless(USER_PROJECT, 'USER_PROJECT not set in environment.') - def test_copy_existing_file_with_user_project(self): - new_bucket_name = 'copy-w-requester-pays' + unique_resource_id('-') - created = Config.CLIENT.create_bucket( - new_bucket_name, requester_pays=True) - self.case_buckets_to_delete.append(new_bucket_name) - self.assertEqual(created.name, new_bucket_name) - self.assertTrue(created.requester_pays) - - to_delete = [] - blob = storage.Blob('simple', bucket=created) - blob.upload_from_string(b'DEADBEEF') - to_delete.append(blob) - try: - with_user_project = Config.CLIENT.bucket( - new_bucket_name, user_project=USER_PROJECT) - - new_blob = retry_bad_copy(with_user_project.copy_blob)( - blob, with_user_project, 'simple-copy') - to_delete.append(new_blob) - - base_contents = blob.download_as_string() - copied_contents = new_blob.download_as_string() - self.assertEqual(base_contents, copied_contents) - finally: - for blob in to_delete: - retry_429(blob.delete)() - - @unittest.skipUnless(USER_PROJECT, 'USER_PROJECT not set in environment.') - def test_bucket_get_blob_with_user_project(self): - new_bucket_name = 'w-requester-pays' + unique_resource_id('-') - data = b'DEADBEEF' - created = Config.CLIENT.create_bucket( - new_bucket_name, requester_pays=True) - self.case_buckets_to_delete.append(new_bucket_name) - self.assertEqual(created.name, new_bucket_name) - self.assertTrue(created.requester_pays) - - with_user_project = Config.CLIENT.bucket( - new_bucket_name, user_project=USER_PROJECT) - - self.assertIsNone(with_user_project.get_blob('nonesuch')) - to_add = created.blob('blob-name') - to_add.upload_from_string(data) - try: - found = with_user_project.get_blob('blob-name') - self.assertEqual(found.download_as_string(), data) - finally: - to_add.delete() - class TestStorageFiles(unittest.TestCase): @@ -348,66 +217,6 @@ def test_small_file_write_from_filename(self): md5_hash = md5_hash.encode('utf-8') self.assertEqual(md5_hash, file_data['hash']) - @unittest.skipUnless(USER_PROJECT, 'USER_PROJECT not set in environment.') - def test_crud_blob_w_user_project(self): - with_user_project = Config.CLIENT.bucket( - self.bucket.name, user_project=USER_PROJECT) - blob = with_user_project.blob('SmallFile') - - file_data = self.FILES['simple'] - with open(file_data['path'], mode='rb') as to_read: - file_contents = to_read.read() - - # Exercise 'objects.insert' w/ userProject. - blob.upload_from_filename(file_data['path']) - - try: - # Exercise 'objects.get' (metadata) w/ userProject. - self.assertTrue(blob.exists()) - blob.reload() - - # Exercise 'objects.get' (media) w/ userProject. - downloaded = blob.download_as_string() - self.assertEqual(downloaded, file_contents) - - # Exercise 'objects.patch' w/ userProject. - blob.content_language = 'en' - blob.patch() - self.assertEqual(blob.content_language, 'en') - - # Exercise 'objects.update' w/ userProject. - metadata = { - 'foo': 'Foo', - 'bar': 'Bar', - } - blob.metadata = metadata - blob.update() - self.assertEqual(blob.metadata, metadata) - finally: - # Exercise 'objects.delete' (metadata) w/ userProject. - blob.delete() - - @unittest.skipUnless(USER_PROJECT, 'USER_PROJECT not set in environment.') - def test_blob_acl_w_user_project(self): - with_user_project = Config.CLIENT.bucket( - self.bucket.name, user_project=USER_PROJECT) - blob = with_user_project.blob('SmallFile') - - file_data = self.FILES['simple'] - - blob.upload_from_filename(file_data['path']) - self.case_blobs_to_delete.append(blob) - - # Exercise bucket ACL w/ userProject - acl = blob.acl - acl.reload() - acl.all().grant_read() - acl.save() - self.assertIn('READER', acl.all().get_roles()) - del acl.entities['allUsers'] - acl.save() - self.assertFalse(acl.has_entity('allUsers')) - def test_write_metadata(self): filename = self.FILES['logo']['path'] blob_name = os.path.basename(filename) @@ -506,15 +315,6 @@ def test_list_files(self): self.assertEqual(sorted(blob.name for blob in all_blobs), sorted(self.FILENAMES)) - @unittest.skipUnless(USER_PROJECT, 'USER_PROJECT not set in environment.') - @RetryErrors(unittest.TestCase.failureException) - def test_list_files_with_user_project(self): - with_user_project = Config.CLIENT.bucket( - self.bucket.name, user_project=USER_PROJECT) - all_blobs = list(with_user_project.list_blobs()) - self.assertEqual(sorted(blob.name for blob in all_blobs), - sorted(self.FILENAMES)) - @RetryErrors(unittest.TestCase.failureException) def test_paginate_files(self): truncation_size = 1 @@ -639,7 +439,7 @@ def tearDown(self): def test_create_signed_read_url(self): blob = self.bucket.blob('LogoToSign.jpg') - expiration = int(time.time() + 10) + expiration = int(time.time() + 5) signed_url = blob.generate_signed_url(expiration, method='GET', client=Config.CLIENT) @@ -702,32 +502,6 @@ def test_compose_replace_existing_blob(self): composed = original.download_as_string() self.assertEqual(composed, BEFORE + TO_APPEND) - @unittest.skipUnless(USER_PROJECT, 'USER_PROJECT not set in environment.') - def test_compose_with_user_project(self): - new_bucket_name = 'compose-user-project' + unique_resource_id('-') - created = Config.CLIENT.create_bucket( - new_bucket_name, requester_pays=True) - try: - SOURCE_1 = b'AAA\n' - source_1 = created.blob('source-1') - source_1.upload_from_string(SOURCE_1) - - SOURCE_2 = b'BBB\n' - source_2 = created.blob('source-2') - source_2.upload_from_string(SOURCE_2) - - with_user_project = Config.CLIENT.bucket( - new_bucket_name, user_project=USER_PROJECT) - - destination = with_user_project.blob('destination') - destination.content_type = 'text/plain' - destination.compose([source_1, source_2]) - - composed = destination.download_as_string() - self.assertEqual(composed, SOURCE_1 + SOURCE_2) - finally: - retry_429(created.delete)(force=True) - class TestStorageRewrite(TestStorageFiles): @@ -771,184 +545,8 @@ def test_rewrite_rotate_encryption_key(self): # Not adding 'dest' to 'self.case_blobs_to_delete': it is the # same object as 'source'. - self.assertIsNone(token) + self.assertEqual(token, None) self.assertEqual(rewritten, len(source_data)) self.assertEqual(total, len(source_data)) self.assertEqual(dest.download_as_string(), source_data) - - @unittest.skipUnless(USER_PROJECT, 'USER_PROJECT not set in environment.') - def test_rewrite_add_key_with_user_project(self): - file_data = self.FILES['simple'] - new_bucket_name = 'rewrite-key-up' + unique_resource_id('-') - created = Config.CLIENT.create_bucket( - new_bucket_name, requester_pays=True) - try: - with_user_project = Config.CLIENT.bucket( - new_bucket_name, user_project=USER_PROJECT) - - source = with_user_project.blob('source') - source.upload_from_filename(file_data['path']) - source_data = source.download_as_string() - - KEY = os.urandom(32) - dest = with_user_project.blob('dest', encryption_key=KEY) - token, rewritten, total = dest.rewrite(source) - - self.assertEqual(token, None) - self.assertEqual(rewritten, len(source_data)) - self.assertEqual(total, len(source_data)) - - self.assertEqual(source.download_as_string(), - dest.download_as_string()) - finally: - retry_429(created.delete)(force=True) - - @unittest.skipUnless(USER_PROJECT, 'USER_PROJECT not set in environment.') - def test_rewrite_rotate_with_user_project(self): - BLOB_NAME = 'rotating-keys' - file_data = self.FILES['simple'] - new_bucket_name = 'rewrite-rotate-up' + unique_resource_id('-') - created = Config.CLIENT.create_bucket( - new_bucket_name, requester_pays=True) - try: - with_user_project = Config.CLIENT.bucket( - new_bucket_name, user_project=USER_PROJECT) - - SOURCE_KEY = os.urandom(32) - source = with_user_project.blob( - BLOB_NAME, encryption_key=SOURCE_KEY) - source.upload_from_filename(file_data['path']) - source_data = source.download_as_string() - - DEST_KEY = os.urandom(32) - dest = with_user_project.blob(BLOB_NAME, encryption_key=DEST_KEY) - token, rewritten, total = dest.rewrite(source) - - self.assertEqual(token, None) - self.assertEqual(rewritten, len(source_data)) - self.assertEqual(total, len(source_data)) - - self.assertEqual(dest.download_as_string(), source_data) - finally: - retry_429(created.delete)(force=True) - - -class TestStorageNotificationCRUD(unittest.TestCase): - - topic = None - TOPIC_NAME = 'notification' + unique_resource_id('-') - CUSTOM_ATTRIBUTES = { - 'attr1': 'value1', - 'attr2': 'value2', - } - BLOB_NAME_PREFIX = 'blob-name-prefix/' - - @property - def topic_path(self): - return 'projects/{}/topics/{}'.format( - Config.CLIENT.project, self.TOPIC_NAME) - - def _intialize_topic(self): - try: - from google.cloud.pubsub_v1 import PublisherClient - except ImportError: - raise unittest.SkipTest("Cannot import pubsub") - self.publisher_client = PublisherClient() - retry_429(self.publisher_client.create_topic)(self.topic_path) - policy = self.publisher_client.get_iam_policy(self.topic_path) - binding = policy.bindings.add() - binding.role = 'roles/pubsub.publisher' - binding.members.append( - 'serviceAccount:{}' - '@gs-project-accounts.iam.gserviceaccount.com'.format( - Config.CLIENT.project)) - self.publisher_client.set_iam_policy(self.topic_path, policy) - - - def setUp(self): - self.case_buckets_to_delete = [] - self._intialize_topic() - - def tearDown(self): - retry_429(self.publisher_client.delete_topic)(self.topic_path) - with Config.CLIENT.batch(): - for bucket_name in self.case_buckets_to_delete: - bucket = Config.CLIENT.bucket(bucket_name) - retry_429(bucket.delete)() - - @staticmethod - def event_types(): - from google.cloud.storage.notification import ( - OBJECT_FINALIZE_EVENT_TYPE, - OBJECT_DELETE_EVENT_TYPE) - - return [OBJECT_FINALIZE_EVENT_TYPE, OBJECT_DELETE_EVENT_TYPE] - - @staticmethod - def payload_format(): - from google.cloud.storage.notification import ( - JSON_API_V1_PAYLOAD_FORMAT) - - return JSON_API_V1_PAYLOAD_FORMAT - - def test_notification_minimal(self): - new_bucket_name = 'notification-minimal' + unique_resource_id('-') - bucket = retry_429(Config.CLIENT.create_bucket)(new_bucket_name) - self.case_buckets_to_delete.append(new_bucket_name) - self.assertEqual(list(bucket.list_notifications()), []) - notification = bucket.notification(self.TOPIC_NAME) - retry_429(notification.create)() - try: - self.assertTrue(notification.exists()) - self.assertIsNotNone(notification.notification_id) - notifications = list(bucket.list_notifications()) - self.assertEqual(len(notifications), 1) - self.assertEqual(notifications[0].topic_name, self.TOPIC_NAME) - finally: - notification.delete() - - def test_notification_explicit(self): - new_bucket_name = 'notification-explicit' + unique_resource_id('-') - bucket = retry_429(Config.CLIENT.create_bucket)(new_bucket_name) - self.case_buckets_to_delete.append(new_bucket_name) - notification = bucket.notification( - self.TOPIC_NAME, - custom_attributes=self.CUSTOM_ATTRIBUTES, - event_types=self.event_types(), - blob_name_prefix=self.BLOB_NAME_PREFIX, - payload_format=self.payload_format(), - ) - retry_429(notification.create)() - try: - self.assertTrue(notification.exists()) - self.assertIsNotNone(notification.notification_id) - self.assertEqual( - notification.custom_attributes, self.CUSTOM_ATTRIBUTES) - self.assertEqual(notification.event_types, self.event_types()) - self.assertEqual( - notification.blob_name_prefix, self.BLOB_NAME_PREFIX) - self.assertEqual( - notification.payload_format, self.payload_format()) - finally: - notification.delete() - - @unittest.skipUnless(USER_PROJECT, 'USER_PROJECT not set in environment.') - def test_notification_w_user_project(self): - new_bucket_name = 'notification-minimal' + unique_resource_id('-') - bucket = retry_429(Config.CLIENT.create_bucket)( - new_bucket_name, requester_pays=True) - self.case_buckets_to_delete.append(new_bucket_name) - with_user_project = Config.CLIENT.bucket( - new_bucket_name, user_project=USER_PROJECT) - self.assertEqual(list(with_user_project.list_notifications()), []) - notification = with_user_project.notification(self.TOPIC_NAME) - retry_429(notification.create)() - try: - self.assertTrue(notification.exists()) - self.assertIsNotNone(notification.notification_id) - notifications = list(with_user_project.list_notifications()) - self.assertEqual(len(notifications), 1) - self.assertEqual(notifications[0].topic_name, self.TOPIC_NAME) - finally: - notification.delete() diff --git a/storage/tests/unit/test__helpers.py b/storage/tests/unit/test__helpers.py index 78bf5fcf3d0a..90def4867268 100644 --- a/storage/tests/unit/test__helpers.py +++ b/storage/tests/unit/test__helpers.py @@ -26,7 +26,7 @@ def _get_target_class(): def _make_one(self, *args, **kw): return self._get_target_class()(*args, **kw) - def _derivedClass(self, path=None, user_project=None): + def _derivedClass(self, path=None): class Derived(self._get_target_class()): @@ -36,67 +36,30 @@ class Derived(self._get_target_class()): def path(self): return path - @property - def user_project(self): - return user_project - return Derived def test_path_is_abstract(self): mixin = self._make_one() - with self.assertRaises(NotImplementedError): - mixin.path + self.assertRaises(NotImplementedError, lambda: mixin.path) def test_client_is_abstract(self): mixin = self._make_one() - with self.assertRaises(NotImplementedError): - mixin.client - - def test_user_project_is_abstract(self): - mixin = self._make_one() - with self.assertRaises(NotImplementedError): - mixin.user_project + self.assertRaises(NotImplementedError, lambda: mixin.client) def test_reload(self): connection = _Connection({'foo': 'Foo'}) client = _Client(connection) derived = self._derivedClass('/path')() - # Make sure changes is not a set instance before calling reload - # (which will clear / replace it with an empty set), checked below. - derived._changes = object() - derived.reload(client=client) - self.assertEqual(derived._properties, {'foo': 'Foo'}) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'GET', - 'path': '/path', - 'query_params': {'projection': 'noAcl'}, - '_target_object': derived, - }) - self.assertEqual(derived._changes, set()) - - def test_reload_w_user_project(self): - user_project = 'user-project-123' - connection = _Connection({'foo': 'Foo'}) - client = _Client(connection) - derived = self._derivedClass('/path', user_project)() - # Make sure changes is not a set instance before calling reload - # (which will clear / replace it with an empty set), checked below. + # Make sure changes is not a set, so we can observe a change. derived._changes = object() derived.reload(client=client) self.assertEqual(derived._properties, {'foo': 'Foo'}) kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'GET', - 'path': '/path', - 'query_params': { - 'projection': 'noAcl', - 'userProject': user_project, - }, - '_target_object': derived, - }) + self.assertEqual(kw[0]['method'], 'GET') + self.assertEqual(kw[0]['path'], '/path') + self.assertEqual(kw[0]['query_params'], {'projection': 'noAcl'}) + # Make sure changes get reset by reload. self.assertEqual(derived._changes, set()) def test__set_properties(self): @@ -124,42 +87,11 @@ def test_patch(self): self.assertEqual(derived._properties, {'foo': 'Foo'}) kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'PATCH', - 'path': '/path', - 'query_params': {'projection': 'full'}, - # Since changes does not include `baz`, we don't see it sent. - 'data': {'bar': BAR}, - '_target_object': derived, - }) - # Make sure changes get reset by patch(). - self.assertEqual(derived._changes, set()) - - def test_patch_w_user_project(self): - user_project = 'user-project-123' - connection = _Connection({'foo': 'Foo'}) - client = _Client(connection) - derived = self._derivedClass('/path', user_project)() - # Make sure changes is non-empty, so we can observe a change. - BAR = object() - BAZ = object() - derived._properties = {'bar': BAR, 'baz': BAZ} - derived._changes = set(['bar']) # Ignore baz. - derived.patch(client=client) - self.assertEqual(derived._properties, {'foo': 'Foo'}) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'PATCH', - 'path': '/path', - 'query_params': { - 'projection': 'full', - 'userProject': user_project, - }, - # Since changes does not include `baz`, we don't see it sent. - 'data': {'bar': BAR}, - '_target_object': derived, - }) + self.assertEqual(kw[0]['method'], 'PATCH') + self.assertEqual(kw[0]['path'], '/path') + self.assertEqual(kw[0]['query_params'], {'projection': 'full'}) + # Since changes does not include `baz`, we don't see it sent. + self.assertEqual(kw[0]['data'], {'bar': BAR}) # Make sure changes get reset by patch(). self.assertEqual(derived._changes, set()) @@ -183,31 +115,6 @@ def test_update(self): # Make sure changes get reset by patch(). self.assertEqual(derived._changes, set()) - def test_update_w_user_project(self): - user_project = 'user-project-123' - connection = _Connection({'foo': 'Foo'}) - client = _Client(connection) - derived = self._derivedClass('/path', user_project)() - # Make sure changes is non-empty, so we can observe a change. - BAR = object() - BAZ = object() - derived._properties = {'bar': BAR, 'baz': BAZ} - derived._changes = set(['bar']) # Update sends 'baz' anyway. - derived.update(client=client) - self.assertEqual(derived._properties, {'foo': 'Foo'}) - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]['method'], 'PUT') - self.assertEqual(kw[0]['path'], '/path') - self.assertEqual( - kw[0]['query_params'], { - 'projection': 'full', - 'userProject': user_project, - }) - self.assertEqual(kw[0]['data'], {'bar': BAR, 'baz': BAZ}) - # Make sure changes get reset by patch(). - self.assertEqual(derived._changes, set()) - class Test__scalar_property(unittest.TestCase): diff --git a/storage/tests/unit/test_acl.py b/storage/tests/unit/test_acl.py index 4e4018ae7c8c..1159c8c1f2aa 100644 --- a/storage/tests/unit/test_acl.py +++ b/storage/tests/unit/test_acl.py @@ -532,11 +532,8 @@ def test_reload_missing(self): self.assertEqual(list(acl), []) kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'GET', - 'path': '/testing/acl', - 'query_params': {}, - }) + self.assertEqual(kw[0]['method'], 'GET') + self.assertEqual(kw[0]['path'], '/testing/acl') def test_reload_empty_result_clears_local(self): ROLE = 'role' @@ -546,41 +543,29 @@ def test_reload_empty_result_clears_local(self): acl.reload_path = '/testing/acl' acl.loaded = True acl.entity('allUsers', ROLE) - acl.reload(client=client) - self.assertTrue(acl.loaded) self.assertEqual(list(acl), []) kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'GET', - 'path': '/testing/acl', - 'query_params': {}, - }) + self.assertEqual(kw[0]['method'], 'GET') + self.assertEqual(kw[0]['path'], '/testing/acl') - def test_reload_nonempty_result_w_user_project(self): + def test_reload_nonempty_result(self): ROLE = 'role' - USER_PROJECT = 'user-project-123' connection = _Connection( {'items': [{'entity': 'allUsers', 'role': ROLE}]}) client = _Client(connection) acl = self._make_one() acl.reload_path = '/testing/acl' acl.loaded = True - acl.user_project = USER_PROJECT - acl.reload(client=client) - self.assertTrue(acl.loaded) self.assertEqual(list(acl), [{'entity': 'allUsers', 'role': ROLE}]) kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'GET', - 'path': '/testing/acl', - 'query_params': {'userProject': USER_PROJECT}, - }) + self.assertEqual(kw[0]['method'], 'GET') + self.assertEqual(kw[0]['path'], '/testing/acl') def test_save_none_set_none_passed(self): connection = _Connection() @@ -621,43 +606,30 @@ def test_save_no_acl(self): self.assertEqual(len(kw), 1) self.assertEqual(kw[0]['method'], 'PATCH') self.assertEqual(kw[0]['path'], '/testing') - self.assertEqual(kw[0], { - 'method': 'PATCH', - 'path': '/testing', - 'query_params': {'projection': 'full'}, - 'data': {'acl': AFTER}, - }) - - def test_save_w_acl_w_user_project(self): + self.assertEqual(kw[0]['data'], {'acl': AFTER}) + self.assertEqual(kw[0]['query_params'], {'projection': 'full'}) + + def test_save_w_acl(self): ROLE1 = 'role1' ROLE2 = 'role2' STICKY = {'entity': 'allUsers', 'role': ROLE2} - USER_PROJECT = 'user-project-123' new_acl = [{'entity': 'allUsers', 'role': ROLE1}] connection = _Connection({'acl': [STICKY] + new_acl}) client = _Client(connection) acl = self._make_one() acl.save_path = '/testing' acl.loaded = True - acl.user_project = USER_PROJECT - acl.save(new_acl, client=client) - entries = list(acl) self.assertEqual(len(entries), 2) self.assertTrue(STICKY in entries) self.assertTrue(new_acl[0] in entries) kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'PATCH', - 'path': '/testing', - 'query_params': { - 'projection': 'full', - 'userProject': USER_PROJECT, - }, - 'data': {'acl': new_acl}, - }) + self.assertEqual(kw[0]['method'], 'PATCH') + self.assertEqual(kw[0]['path'], '/testing') + self.assertEqual(kw[0]['data'], {'acl': new_acl}) + self.assertEqual(kw[0]['query_params'], {'projection': 'full'}) def test_save_prefefined_invalid(self): connection = _Connection() @@ -680,15 +652,11 @@ def test_save_predefined_valid(self): self.assertEqual(len(entries), 0) kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'PATCH', - 'path': '/testing', - 'query_params': { - 'projection': 'full', - 'predefinedAcl': PREDEFINED, - }, - 'data': {'acl': []}, - }) + self.assertEqual(kw[0]['method'], 'PATCH') + self.assertEqual(kw[0]['path'], '/testing') + self.assertEqual(kw[0]['data'], {'acl': []}) + self.assertEqual(kw[0]['query_params'], + {'projection': 'full', 'predefinedAcl': PREDEFINED}) def test_save_predefined_w_XML_alias(self): PREDEFINED_XML = 'project-private' @@ -703,15 +671,12 @@ def test_save_predefined_w_XML_alias(self): self.assertEqual(len(entries), 0) kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'PATCH', - 'path': '/testing', - 'query_params': { - 'projection': 'full', - 'predefinedAcl': PREDEFINED_JSON, - }, - 'data': {'acl': []}, - }) + self.assertEqual(kw[0]['method'], 'PATCH') + self.assertEqual(kw[0]['path'], '/testing') + self.assertEqual(kw[0]['data'], {'acl': []}) + self.assertEqual(kw[0]['query_params'], + {'projection': 'full', + 'predefinedAcl': PREDEFINED_JSON}) def test_save_predefined_valid_w_alternate_query_param(self): # Cover case where subclass overrides _PREDEFINED_QUERY_PARAM @@ -727,15 +692,11 @@ def test_save_predefined_valid_w_alternate_query_param(self): self.assertEqual(len(entries), 0) kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'PATCH', - 'path': '/testing', - 'query_params': { - 'projection': 'full', - 'alternate': PREDEFINED, - }, - 'data': {'acl': []}, - }) + self.assertEqual(kw[0]['method'], 'PATCH') + self.assertEqual(kw[0]['path'], '/testing') + self.assertEqual(kw[0]['data'], {'acl': []}) + self.assertEqual(kw[0]['query_params'], + {'projection': 'full', 'alternate': PREDEFINED}) def test_clear(self): ROLE1 = 'role1' @@ -751,12 +712,10 @@ def test_clear(self): self.assertEqual(list(acl), [STICKY]) kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'PATCH', - 'path': '/testing', - 'query_params': {'projection': 'full'}, - 'data': {'acl': []}, - }) + self.assertEqual(kw[0]['method'], 'PATCH') + self.assertEqual(kw[0]['path'], '/testing') + self.assertEqual(kw[0]['data'], {'acl': []}) + self.assertEqual(kw[0]['query_params'], {'projection': 'full'}) class Test_BucketACL(unittest.TestCase): @@ -780,15 +739,6 @@ def test_ctor(self): self.assertEqual(acl.reload_path, '/b/%s/acl' % NAME) self.assertEqual(acl.save_path, '/b/%s' % NAME) - def test_user_project(self): - NAME = 'name' - USER_PROJECT = 'user-project-123' - bucket = _Bucket(NAME) - acl = self._make_one(bucket) - self.assertIsNone(acl.user_project) - bucket.user_project = USER_PROJECT - self.assertEqual(acl.user_project, USER_PROJECT) - class Test_DefaultObjectACL(unittest.TestCase): @@ -835,22 +785,9 @@ def test_ctor(self): self.assertEqual(acl.reload_path, '/b/%s/o/%s/acl' % (NAME, BLOB_NAME)) self.assertEqual(acl.save_path, '/b/%s/o/%s' % (NAME, BLOB_NAME)) - def test_user_project(self): - NAME = 'name' - BLOB_NAME = 'blob-name' - USER_PROJECT = 'user-project-123' - bucket = _Bucket(NAME) - blob = _Blob(bucket, BLOB_NAME) - acl = self._make_one(blob) - self.assertIsNone(acl.user_project) - blob.user_project = USER_PROJECT - self.assertEqual(acl.user_project, USER_PROJECT) - class _Blob(object): - user_project = None - def __init__(self, bucket, blob): self.bucket = bucket self.blob = blob @@ -862,8 +799,6 @@ def path(self): class _Bucket(object): - user_project = None - def __init__(self, name): self.name = name diff --git a/storage/tests/unit/test_blob.py b/storage/tests/unit/test_blob.py index a363bc2a09d8..43cbf03a51ed 100644 --- a/storage/tests/unit/test_blob.py +++ b/storage/tests/unit/test_blob.py @@ -144,19 +144,6 @@ def test_path_with_non_ascii(self): blob = self._make_one(blob_name, bucket=bucket) self.assertEqual(blob.path, '/b/name/o/Caf%C3%A9') - def test_client(self): - blob_name = 'BLOB' - bucket = _Bucket() - blob = self._make_one(blob_name, bucket=bucket) - self.assertIs(blob.client, bucket.client) - - def test_user_project(self): - user_project = 'user-project-123' - blob_name = 'BLOB' - bucket = _Bucket(user_project=user_project) - blob = self._make_one(blob_name, bucket=bucket) - self.assertEqual(blob.user_project, user_project) - def test_public_url(self): BLOB_NAME = 'blob-name' bucket = _Bucket() @@ -320,31 +307,16 @@ def test_exists_miss(self): bucket = _Bucket(client) blob = self._make_one(NONESUCH, bucket=bucket) self.assertFalse(blob.exists()) - self.assertEqual(len(connection._requested), 1) - self.assertEqual(connection._requested[0], { - 'method': 'GET', - 'path': '/b/name/o/{}'.format(NONESUCH), - 'query_params': {'fields': 'name'}, - '_target_object': None, - }) - def test_exists_hit_w_user_project(self): + def test_exists_hit(self): BLOB_NAME = 'blob-name' - USER_PROJECT = 'user-project-123' found_response = ({'status': http_client.OK}, b'') connection = _Connection(found_response) client = _Client(connection) - bucket = _Bucket(client, user_project=USER_PROJECT) + bucket = _Bucket(client) blob = self._make_one(BLOB_NAME, bucket=bucket) bucket._blobs[BLOB_NAME] = 1 self.assertTrue(blob.exists()) - self.assertEqual(len(connection._requested), 1) - self.assertEqual(connection._requested[0], { - 'method': 'GET', - 'path': '/b/name/o/{}'.format(BLOB_NAME), - 'query_params': {'fields': 'name', 'userProject': USER_PROJECT}, - '_target_object': None, - }) def test_delete(self): BLOB_NAME = 'blob-name' @@ -369,7 +341,7 @@ def test__get_transport(self): def test__get_download_url_with_media_link(self): blob_name = 'something.txt' - bucket = _Bucket(name='IRRELEVANT') + bucket = mock.Mock(spec=[]) blob = self._make_one(blob_name, bucket=bucket) media_link = 'http://test.invalid' # Set the media link on the blob @@ -378,22 +350,9 @@ def test__get_download_url_with_media_link(self): download_url = blob._get_download_url() self.assertEqual(download_url, media_link) - def test__get_download_url_with_media_link_w_user_project(self): - blob_name = 'something.txt' - user_project = 'user-project-123' - bucket = _Bucket(name='IRRELEVANT', user_project=user_project) - blob = self._make_one(blob_name, bucket=bucket) - media_link = 'http://test.invalid' - # Set the media link on the blob - blob._properties['mediaLink'] = media_link - - download_url = blob._get_download_url() - self.assertEqual( - download_url, '{}?userProject={}'.format(media_link, user_project)) - def test__get_download_url_on_the_fly(self): blob_name = 'bzzz-fly.txt' - bucket = _Bucket(name='buhkit') + bucket = mock.Mock(path='/b/buhkit', spec=['path']) blob = self._make_one(blob_name, bucket=bucket) self.assertIsNone(blob.media_link) @@ -405,7 +364,7 @@ def test__get_download_url_on_the_fly(self): def test__get_download_url_on_the_fly_with_generation(self): blob_name = 'pretend.txt' - bucket = _Bucket(name='fictional') + bucket = mock.Mock(path='/b/fictional', spec=['path']) blob = self._make_one(blob_name, bucket=bucket) generation = 1493058489532987 # Set the media link on the blob @@ -418,20 +377,6 @@ def test__get_download_url_on_the_fly_with_generation(self): 'fictional/o/pretend.txt?alt=media&generation=1493058489532987') self.assertEqual(download_url, expected_url) - def test__get_download_url_on_the_fly_with_user_project(self): - blob_name = 'pretend.txt' - user_project = 'user-project-123' - bucket = _Bucket(name='fictional', user_project=user_project) - blob = self._make_one(blob_name, bucket=bucket) - - self.assertIsNone(blob.media_link) - download_url = blob._get_download_url() - expected_url = ( - 'https://www.googleapis.com/download/storage/v1/b/' - 'fictional/o/pretend.txt?alt=media&userProject={}'.format( - user_project)) - self.assertEqual(download_url, expected_url) - @staticmethod def _mock_requests_response( status_code, headers, content=b'', stream=False): @@ -475,7 +420,6 @@ def _check_session_mocks(self, client, transport, # **MUTABLE** headers and it was mutated before the # second request. headers['range'] = 'bytes=3-5' - headers['accept-encoding'] = 'gzip' call = mock.call( 'GET', expected_url, data=None, headers=headers) self.assertEqual(transport.request.mock_calls, [call, call]) @@ -561,8 +505,7 @@ def test_download_to_file_with_failure(self): self.assertEqual(file_obj.tell(), 0) # Check that the transport was called once. transport.request.assert_called_once_with( - 'GET', blob.media_link, data=None, - headers={'accept-encoding': 'gzip'}, stream=True) + 'GET', blob.media_link, data=None, headers={}, stream=True) def test_download_to_file_wo_media_link(self): blob_name = 'blob-name' @@ -617,8 +560,7 @@ def _download_to_file_helper(self, use_chunks=False): self._check_session_mocks(client, transport, media_link) else: transport.request.assert_called_once_with( - 'GET', media_link, data=None, - headers={'accept-encoding': 'gzip'}, stream=True) + 'GET', media_link, data=None, headers={}, stream=True) def test_download_to_file_default(self): self._download_to_file_helper() @@ -736,7 +678,7 @@ def test_download_to_filename_corrupted(self): 'GET', media_link, data=None, - headers={'accept-encoding': 'gzip'}, + headers={}, stream=True, ) @@ -755,8 +697,7 @@ def test_download_to_filename_w_key(self): 'updated': '2014-12-06T13:13:50.690Z'} key = b'aa426195405adee2c8081bb9e7e74b19' blob = self._make_one( - blob_name, bucket=bucket, properties=properties, - encryption_key=key) + blob_name, bucket=bucket, properties=properties, encryption_key=key) # Modify the blob so there there will be 2 chunks of size 3. blob._CHUNK_SIZE_MULTIPLE = 1 blob.chunk_size = 3 @@ -777,7 +718,6 @@ def test_download_to_filename_w_key(self): 'X-Goog-Encryption-Key-Sha256': header_key_hash_value, 'X-Goog-Encryption-Algorithm': 'AES256', 'X-Goog-Encryption-Key': header_key_value, - 'accept-encoding': 'gzip', } self._check_session_mocks( client, transport, media_link, headers=key_headers) @@ -894,8 +834,8 @@ def _mock_transport(self, status_code, headers, content=b''): return fake_transport def _do_multipart_success(self, mock_get_boundary, size=None, - num_retries=None, user_project=None): - bucket = _Bucket(name='w00t', user_project=user_project) + num_retries=None): + bucket = mock.Mock(path='/b/w00t', spec=[u'path']) blob = self._make_one(u'blob-name', bucket=bucket) self.assertIsNone(blob.chunk_size) @@ -925,8 +865,6 @@ def _do_multipart_success(self, mock_get_boundary, size=None, 'https://www.googleapis.com/upload/storage/v1' + bucket.path + '/o?uploadType=multipart') - if user_project is not None: - upload_url += '&userProject={}'.format(user_project) payload = ( b'--==0==\r\n' + b'content-type: application/json; charset=UTF-8\r\n\r\n' + @@ -949,13 +887,6 @@ def test__do_multipart_upload_no_size(self, mock_get_boundary): def test__do_multipart_upload_with_size(self, mock_get_boundary): self._do_multipart_success(mock_get_boundary, size=10) - @mock.patch(u'google.resumable_media._upload.get_boundary', - return_value=b'==0==') - def test__do_multipart_upload_with_user_project(self, mock_get_boundary): - user_project = 'user-project-123' - self._do_multipart_success( - mock_get_boundary, user_project=user_project) - @mock.patch(u'google.resumable_media._upload.get_boundary', return_value=b'==0==') def test__do_multipart_upload_with_retry(self, mock_get_boundary): @@ -977,12 +908,11 @@ def test__do_multipart_upload_bad_size(self): 'was specified but the file-like object only had', exc_contents) self.assertEqual(stream.tell(), len(data)) - def _initiate_resumable_helper( - self, size=None, extra_headers=None, chunk_size=None, - num_retries=None, user_project=None): + def _initiate_resumable_helper(self, size=None, extra_headers=None, + chunk_size=None, num_retries=None): from google.resumable_media.requests import ResumableUpload - bucket = _Bucket(name='whammy', user_project=user_project) + bucket = mock.Mock(path='/b/whammy', spec=[u'path']) blob = self._make_one(u'blob-name', bucket=bucket) blob.metadata = {'rook': 'takes knight'} blob.chunk_size = 3 * blob._CHUNK_SIZE_MULTIPLE @@ -1014,8 +944,6 @@ def _initiate_resumable_helper( 'https://www.googleapis.com/upload/storage/v1' + bucket.path + '/o?uploadType=resumable') - if user_project is not None: - upload_url += '&userProject={}'.format(user_project) self.assertEqual(upload.upload_url, upload_url) if extra_headers is None: self.assertEqual(upload._headers, {}) @@ -1067,10 +995,6 @@ def test__initiate_resumable_upload_no_size(self): def test__initiate_resumable_upload_with_size(self): self._initiate_resumable_helper(size=10000) - def test__initiate_resumable_upload_with_user_project(self): - user_project = 'user-project-123' - self._initiate_resumable_helper(user_project=user_project) - def test__initiate_resumable_upload_with_chunk_size(self): one_mb = 1048576 self._initiate_resumable_helper(chunk_size=one_mb) @@ -1151,7 +1075,7 @@ def _do_resumable_upload_call2(blob, content_type, data, 'PUT', resumable_url, data=payload, headers=expected_headers) def _do_resumable_helper(self, use_size=False, num_retries=None): - bucket = _Bucket(name='yesterday') + bucket = mock.Mock(path='/b/yesterday', spec=[u'path']) blob = self._make_one(u'blob-name', bucket=bucket) blob.chunk_size = blob._CHUNK_SIZE_MULTIPLE self.assertIsNotNone(blob.chunk_size) @@ -1396,7 +1320,7 @@ def test_upload_from_string_w_text(self): def _create_resumable_upload_session_helper(self, origin=None, side_effect=None): - bucket = _Bucket(name='alex-trebek') + bucket = mock.Mock(path='/b/alex-trebek', spec=[u'path']) blob = self._make_one('blob-name', bucket=bucket) chunk_size = 99 * blob._CHUNK_SIZE_MULTIPLE blob.chunk_size = chunk_size @@ -1505,49 +1429,8 @@ def test_get_iam_policy(self): kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'GET', - 'path': '%s/iam' % (PATH,), - 'query_params': {}, - '_target_object': None, - }) - - def test_get_iam_policy_w_user_project(self): - from google.cloud.iam import Policy - - BLOB_NAME = 'blob-name' - USER_PROJECT = 'user-project-123' - PATH = '/b/name/o/%s' % (BLOB_NAME,) - ETAG = 'DEADBEEF' - VERSION = 17 - RETURNED = { - 'resourceId': PATH, - 'etag': ETAG, - 'version': VERSION, - 'bindings': [], - } - after = ({'status': http_client.OK}, RETURNED) - EXPECTED = {} - connection = _Connection(after) - client = _Client(connection) - bucket = _Bucket(client=client, user_project=USER_PROJECT) - blob = self._make_one(BLOB_NAME, bucket=bucket) - - policy = blob.get_iam_policy() - - self.assertIsInstance(policy, Policy) - self.assertEqual(policy.etag, RETURNED['etag']) - self.assertEqual(policy.version, RETURNED['version']) - self.assertEqual(dict(policy), EXPECTED) - - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'GET', - 'path': '%s/iam' % (PATH,), - 'query_params': {'userProject': USER_PROJECT}, - '_target_object': None, - }) + self.assertEqual(kw[0]['method'], 'GET') + self.assertEqual(kw[0]['path'], '%s/iam' % (PATH,)) def test_set_iam_policy(self): import operator @@ -1596,53 +1479,17 @@ def test_set_iam_policy(self): self.assertEqual(len(kw), 1) self.assertEqual(kw[0]['method'], 'PUT') self.assertEqual(kw[0]['path'], '%s/iam' % (PATH,)) - self.assertEqual(kw[0]['query_params'], {}) sent = kw[0]['data'] self.assertEqual(sent['resourceId'], PATH) self.assertEqual(len(sent['bindings']), len(BINDINGS)) key = operator.itemgetter('role') for found, expected in zip( - sorted(sent['bindings'], key=key), - sorted(BINDINGS, key=key)): + sorted(sent['bindings'], key=key), + sorted(BINDINGS, key=key)): self.assertEqual(found['role'], expected['role']) self.assertEqual( sorted(found['members']), sorted(expected['members'])) - def test_set_iam_policy_w_user_project(self): - from google.cloud.iam import Policy - - BLOB_NAME = 'blob-name' - USER_PROJECT = 'user-project-123' - PATH = '/b/name/o/%s' % (BLOB_NAME,) - ETAG = 'DEADBEEF' - VERSION = 17 - BINDINGS = [] - RETURNED = { - 'etag': ETAG, - 'version': VERSION, - 'bindings': BINDINGS, - } - after = ({'status': http_client.OK}, RETURNED) - policy = Policy() - - connection = _Connection(after) - client = _Client(connection) - bucket = _Bucket(client=client, user_project=USER_PROJECT) - blob = self._make_one(BLOB_NAME, bucket=bucket) - - returned = blob.set_iam_policy(policy) - - self.assertEqual(returned.etag, ETAG) - self.assertEqual(returned.version, VERSION) - self.assertEqual(dict(returned), dict(policy)) - - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]['method'], 'PUT') - self.assertEqual(kw[0]['path'], '%s/iam' % (PATH,)) - self.assertEqual(kw[0]['query_params'], {'userProject': USER_PROJECT}) - self.assertEqual(kw[0]['data'], {'resourceId': PATH}) - def test_test_iam_permissions(self): from google.cloud.storage.iam import STORAGE_OBJECTS_LIST from google.cloud.storage.iam import STORAGE_BUCKETS_GET @@ -1673,39 +1520,6 @@ def test_test_iam_permissions(self): self.assertEqual(kw[0]['path'], '%s/iam/testPermissions' % (PATH,)) self.assertEqual(kw[0]['query_params'], {'permissions': PERMISSIONS}) - def test_test_iam_permissions_w_user_project(self): - from google.cloud.storage.iam import STORAGE_OBJECTS_LIST - from google.cloud.storage.iam import STORAGE_BUCKETS_GET - from google.cloud.storage.iam import STORAGE_BUCKETS_UPDATE - - BLOB_NAME = 'blob-name' - USER_PROJECT = 'user-project-123' - PATH = '/b/name/o/%s' % (BLOB_NAME,) - PERMISSIONS = [ - STORAGE_OBJECTS_LIST, - STORAGE_BUCKETS_GET, - STORAGE_BUCKETS_UPDATE, - ] - ALLOWED = PERMISSIONS[1:] - RETURNED = {'permissions': ALLOWED} - after = ({'status': http_client.OK}, RETURNED) - connection = _Connection(after) - client = _Client(connection) - bucket = _Bucket(client=client, user_project=USER_PROJECT) - blob = self._make_one(BLOB_NAME, bucket=bucket) - - allowed = blob.test_iam_permissions(PERMISSIONS) - - self.assertEqual(allowed, ALLOWED) - - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]['method'], 'GET') - self.assertEqual(kw[0]['path'], '%s/iam/testPermissions' % (PATH,)) - self.assertEqual( - kw[0]['query_params'], - {'permissions': PERMISSIONS, 'userProject': USER_PROJECT}) - def test_make_public(self): from google.cloud.storage.acl import _ACLEntity @@ -1740,18 +1554,17 @@ def test_compose_wo_content_type_set(self): with self.assertRaises(ValueError): destination.compose(sources=[source_1, source_2]) - def test_compose_minimal_w_user_project(self): + def test_compose_minimal(self): SOURCE_1 = 'source-1' SOURCE_2 = 'source-2' DESTINATION = 'destinaton' RESOURCE = { 'etag': 'DEADBEEF' } - USER_PROJECT = 'user-project-123' after = ({'status': http_client.OK}, RESOURCE) connection = _Connection(after) client = _Client(connection) - bucket = _Bucket(client=client, user_project=USER_PROJECT) + bucket = _Bucket(client=client) source_1 = self._make_one(SOURCE_1, bucket=bucket) source_2 = self._make_one(SOURCE_2, bucket=bucket) destination = self._make_one(DESTINATION, bucket=bucket) @@ -1761,23 +1574,20 @@ def test_compose_minimal_w_user_project(self): self.assertEqual(destination.etag, 'DEADBEEF') + SENT = { + 'sourceObjects': [ + {'name': source_1.name}, + {'name': source_2.name}, + ], + 'destination': { + 'contentType': 'text/plain', + }, + } kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'POST', - 'path': '/b/name/o/%s/compose' % DESTINATION, - 'query_params': {'userProject': USER_PROJECT}, - 'data': { - 'sourceObjects': [ - {'name': source_1.name}, - {'name': source_2.name}, - ], - 'destination': { - 'contentType': 'text/plain', - }, - }, - '_target_object': destination, - }) + self.assertEqual(kw[0]['method'], 'POST') + self.assertEqual(kw[0]['path'], '/b/name/o/%s/compose' % DESTINATION) + self.assertEqual(kw[0]['data'], SENT) def test_compose_w_additional_property_changes(self): SOURCE_1 = 'source-1' @@ -1801,27 +1611,24 @@ def test_compose_w_additional_property_changes(self): self.assertEqual(destination.etag, 'DEADBEEF') + SENT = { + 'sourceObjects': [ + {'name': source_1.name}, + {'name': source_2.name}, + ], + 'destination': { + 'contentType': 'text/plain', + 'contentLanguage': 'en-US', + 'metadata': { + 'my-key': 'my-value', + } + }, + } kw = connection._requested self.assertEqual(len(kw), 1) - self.assertEqual(kw[0], { - 'method': 'POST', - 'path': '/b/name/o/%s/compose' % DESTINATION, - 'query_params': {}, - 'data': { - 'sourceObjects': [ - {'name': source_1.name}, - {'name': source_2.name}, - ], - 'destination': { - 'contentType': 'text/plain', - 'contentLanguage': 'en-US', - 'metadata': { - 'my-key': 'my-value', - } - }, - }, - '_target_object': destination, - }) + self.assertEqual(kw[0]['method'], 'POST') + self.assertEqual(kw[0]['path'], '/b/name/o/%s/compose' % DESTINATION) + self.assertEqual(kw[0]['data'], SENT) def test_rewrite_response_without_resource(self): SOURCE_BLOB = 'source' @@ -1893,7 +1700,7 @@ def test_rewrite_other_bucket_other_name_no_encryption_partial(self): self.assertNotIn('X-Goog-Encryption-Key', headers) self.assertNotIn('X-Goog-Encryption-Key-Sha256', headers) - def test_rewrite_same_name_no_old_key_new_key_done_w_user_project(self): + def test_rewrite_same_name_no_old_key_new_key_done(self): import base64 import hashlib @@ -1902,7 +1709,6 @@ def test_rewrite_same_name_no_old_key_new_key_done_w_user_project(self): KEY_HASH = hashlib.sha256(KEY).digest() KEY_HASH_B64 = base64.b64encode(KEY_HASH).rstrip().decode('ascii') BLOB_NAME = 'blob' - USER_PROJECT = 'user-project-123' RESPONSE = { 'totalBytesRewritten': 42, 'objectSize': 42, @@ -1912,7 +1718,7 @@ def test_rewrite_same_name_no_old_key_new_key_done_w_user_project(self): response = ({'status': http_client.OK}, RESPONSE) connection = _Connection(response) client = _Client(connection) - bucket = _Bucket(client=client, user_project=USER_PROJECT) + bucket = _Bucket(client=client) plain = self._make_one(BLOB_NAME, bucket=bucket) encrypted = self._make_one(BLOB_NAME, bucket=bucket, encryption_key=KEY) @@ -1928,7 +1734,7 @@ def test_rewrite_same_name_no_old_key_new_key_done_w_user_project(self): self.assertEqual(kw[0]['method'], 'POST') PATH = '/b/name/o/%s/rewriteTo/b/name/o/%s' % (BLOB_NAME, BLOB_NAME) self.assertEqual(kw[0]['path'], PATH) - self.assertEqual(kw[0]['query_params'], {'userProject': USER_PROJECT}) + self.assertEqual(kw[0]['query_params'], {}) SENT = {} self.assertEqual(kw[0]['data'], SENT) @@ -2031,7 +1837,7 @@ def test_update_storage_class_wo_encryption_key(self): self.assertEqual(kw[0]['method'], 'POST') PATH = '/b/name/o/%s/rewriteTo/b/name/o/%s' % (BLOB_NAME, BLOB_NAME) self.assertEqual(kw[0]['path'], PATH) - self.assertEqual(kw[0]['query_params'], {}) + self.assertNotIn('query_params', kw[0]) SENT = {'storageClass': STORAGE_CLASS} self.assertEqual(kw[0]['data'], SENT) @@ -2045,7 +1851,7 @@ def test_update_storage_class_wo_encryption_key(self): self.assertNotIn('X-Goog-Encryption-Key', headers) self.assertNotIn('X-Goog-Encryption-Key-Sha256', headers) - def test_update_storage_class_w_encryption_key_w_user_project(self): + def test_update_storage_class_w_encryption_key(self): import base64 import hashlib @@ -2056,14 +1862,13 @@ def test_update_storage_class_w_encryption_key_w_user_project(self): BLOB_KEY_HASH_B64 = base64.b64encode( BLOB_KEY_HASH).rstrip().decode('ascii') STORAGE_CLASS = u'NEARLINE' - USER_PROJECT = 'user-project-123' RESPONSE = { 'resource': {'storageClass': STORAGE_CLASS}, } response = ({'status': http_client.OK}, RESPONSE) connection = _Connection(response) client = _Client(connection) - bucket = _Bucket(client=client, user_project=USER_PROJECT) + bucket = _Bucket(client=client) blob = self._make_one( BLOB_NAME, bucket=bucket, encryption_key=BLOB_KEY) @@ -2076,7 +1881,7 @@ def test_update_storage_class_w_encryption_key_w_user_project(self): self.assertEqual(kw[0]['method'], 'POST') PATH = '/b/name/o/%s/rewriteTo/b/name/o/%s' % (BLOB_NAME, BLOB_NAME) self.assertEqual(kw[0]['path'], PATH) - self.assertEqual(kw[0]['query_params'], {'userProject': USER_PROJECT}) + self.assertNotIn('query_params', kw[0]) SENT = {'storageClass': STORAGE_CLASS} self.assertEqual(kw[0]['data'], SENT) @@ -2510,37 +2315,6 @@ def test_default(self): self.assertEqual(exc_info.exception.errors, []) -class Test__add_query_parameters(unittest.TestCase): - - @staticmethod - def _call_fut(*args, **kwargs): - from google.cloud.storage.blob import _add_query_parameters - - return _add_query_parameters(*args, **kwargs) - - def test_w_empty_list(self): - BASE_URL = 'https://test.example.com/base' - self.assertEqual(self._call_fut(BASE_URL, []), BASE_URL) - - def test_wo_existing_qs(self): - BASE_URL = 'https://test.example.com/base' - NV_LIST = [('one', 'One'), ('two', 'Two')] - expected = '&'.join([ - '{}={}'.format(name, value) for name, value in NV_LIST]) - self.assertEqual( - self._call_fut(BASE_URL, NV_LIST), - '{}?{}'.format(BASE_URL, expected)) - - def test_w_existing_qs(self): - BASE_URL = 'https://test.example.com/base?one=Three' - NV_LIST = [('one', 'One'), ('two', 'Two')] - expected = '&'.join([ - '{}={}'.format(name, value) for name, value in NV_LIST]) - self.assertEqual( - self._call_fut(BASE_URL, NV_LIST), - '{}&{}'.format(BASE_URL, expected)) - - class _Connection(object): API_BASE_URL = 'http://example.com' @@ -2568,7 +2342,7 @@ def api_request(self, **kw): class _Bucket(object): - def __init__(self, client=None, name='name', user_project=None): + def __init__(self, client=None, name='name'): if client is None: connection = _Connection() client = _Client(connection) @@ -2578,7 +2352,6 @@ def __init__(self, client=None, name='name', user_project=None): self._deleted = [] self.name = name self.path = '/b/' + name - self.user_project = user_project def delete_blob(self, blob_name, client=None): del self._blobs[blob_name] diff --git a/storage/tests/unit/test_bucket.py b/storage/tests/unit/test_bucket.py index 370e401f2bb7..1fd2da128756 100644 --- a/storage/tests/unit/test_bucket.py +++ b/storage/tests/unit/test_bucket.py @@ -33,21 +33,13 @@ class _SigningCredentials( class Test_Bucket(unittest.TestCase): - @staticmethod - def _get_target_class(): + def _make_one(self, client=None, name=None, properties=None): from google.cloud.storage.bucket import Bucket - return Bucket - def _make_one( - self, client=None, name=None, properties=None, user_project=None): if client is None: connection = _Connection() client = _Client(connection) - if user_project is None: - bucket = self._get_target_class()(client, name=name) - else: - bucket = self._get_target_class()( - client, name=name, user_project=user_project) + bucket = Bucket(client, name=name) bucket._properties = properties or {} return bucket @@ -61,21 +53,6 @@ def test_ctor(self): self.assertIs(bucket._acl.bucket, bucket) self.assertFalse(bucket._default_object_acl.loaded) self.assertIs(bucket._default_object_acl.bucket, bucket) - self.assertIsNone(bucket.user_project) - - def test_ctor_w_user_project(self): - NAME = 'name' - USER_PROJECT = 'user-project-123' - connection = _Connection() - client = _Client(connection) - bucket = self._make_one(client, name=NAME, user_project=USER_PROJECT) - self.assertEqual(bucket.name, NAME) - self.assertEqual(bucket._properties, {}) - self.assertEqual(bucket.user_project, USER_PROJECT) - self.assertFalse(bucket._acl.loaded) - self.assertIs(bucket._acl.bucket, bucket) - self.assertFalse(bucket._default_object_acl.loaded) - self.assertIs(bucket._default_object_acl.bucket, bucket) def test_blob(self): from google.cloud.storage.blob import Blob @@ -95,67 +72,10 @@ def test_blob(self): self.assertEqual(blob.chunk_size, CHUNK_SIZE) self.assertEqual(blob._encryption_key, KEY) - def test_notification_defaults(self): - from google.cloud.storage.notification import BucketNotification - from google.cloud.storage.notification import NONE_PAYLOAD_FORMAT - - PROJECT = 'PROJECT' - BUCKET_NAME = 'BUCKET_NAME' - TOPIC_NAME = 'TOPIC_NAME' - client = _Client(_Connection(), project=PROJECT) - bucket = self._make_one(client, name=BUCKET_NAME) - - notification = bucket.notification(TOPIC_NAME) - - self.assertIsInstance(notification, BucketNotification) - self.assertIs(notification.bucket, bucket) - self.assertEqual(notification.topic_project, PROJECT) - self.assertIsNone(notification.custom_attributes) - self.assertIsNone(notification.event_types) - self.assertIsNone(notification.blob_name_prefix) - self.assertEqual(notification.payload_format, NONE_PAYLOAD_FORMAT) - - def test_notification_explicit(self): - from google.cloud.storage.notification import ( - BucketNotification, - OBJECT_FINALIZE_EVENT_TYPE, - OBJECT_DELETE_EVENT_TYPE, - JSON_API_V1_PAYLOAD_FORMAT) - - PROJECT = 'PROJECT' - BUCKET_NAME = 'BUCKET_NAME' - TOPIC_NAME = 'TOPIC_NAME' - TOPIC_ALT_PROJECT = 'topic-project-456' - CUSTOM_ATTRIBUTES = { - 'attr1': 'value1', - 'attr2': 'value2', - } - EVENT_TYPES = [OBJECT_FINALIZE_EVENT_TYPE, OBJECT_DELETE_EVENT_TYPE] - BLOB_NAME_PREFIX = 'blob-name-prefix/' - client = _Client(_Connection(), project=PROJECT) - bucket = self._make_one(client, name=BUCKET_NAME) - - notification = bucket.notification( - TOPIC_NAME, - topic_project=TOPIC_ALT_PROJECT, - custom_attributes=CUSTOM_ATTRIBUTES, - event_types=EVENT_TYPES, - blob_name_prefix=BLOB_NAME_PREFIX, - payload_format=JSON_API_V1_PAYLOAD_FORMAT, - ) - - self.assertIsInstance(notification, BucketNotification) - self.assertIs(notification.bucket, bucket) - self.assertEqual(notification.topic_project, TOPIC_ALT_PROJECT) - self.assertEqual(notification.custom_attributes, CUSTOM_ATTRIBUTES) - self.assertEqual(notification.event_types, EVENT_TYPES) - self.assertEqual(notification.blob_name_prefix, BLOB_NAME_PREFIX) - self.assertEqual( - notification.payload_format, JSON_API_V1_PAYLOAD_FORMAT) - def test_bucket_name_value(self): - BUCKET_NAME = 'bucket-name' - bucket = self._make_one(name=BUCKET_NAME) + bucket_name = 'testing123' + mixin = self._make_one(name=bucket_name) + self.assertEqual(mixin.name, bucket_name) bad_start_bucket_name = '/testing123' with self.assertRaises(ValueError): @@ -165,13 +85,6 @@ def test_bucket_name_value(self): with self.assertRaises(ValueError): self._make_one(name=bad_end_bucket_name) - def test_user_project(self): - BUCKET_NAME = 'name' - USER_PROJECT = 'user-project-123' - bucket = self._make_one(name=BUCKET_NAME) - bucket._user_project = USER_PROJECT - self.assertEqual(bucket.user_project, USER_PROJECT) - def test_exists_miss(self): from google.cloud.exceptions import NotFound @@ -199,9 +112,7 @@ def api_request(cls, *args, **kwargs): expected_cw = [((), expected_called_kwargs)] self.assertEqual(_FakeConnection._called_with, expected_cw) - def test_exists_hit_w_user_project(self): - USER_PROJECT = 'user-project-123' - + def test_exists_hit(self): class _FakeConnection(object): _called_with = [] @@ -213,7 +124,7 @@ def api_request(cls, *args, **kwargs): return object() BUCKET_NAME = 'bucket-name' - bucket = self._make_one(name=BUCKET_NAME, user_project=USER_PROJECT) + bucket = self._make_one(name=BUCKET_NAME) client = _Client(_FakeConnection) self.assertTrue(bucket.exists(client=client)) expected_called_kwargs = { @@ -221,29 +132,17 @@ def api_request(cls, *args, **kwargs): 'path': bucket.path, 'query_params': { 'fields': 'name', - 'userProject': USER_PROJECT, }, '_target_object': None, } expected_cw = [((), expected_called_kwargs)] self.assertEqual(_FakeConnection._called_with, expected_cw) - def test_create_w_user_project(self): - PROJECT = 'PROJECT' - BUCKET_NAME = 'bucket-name' - USER_PROJECT = 'user-project-123' - connection = _Connection() - client = _Client(connection, project=PROJECT) - bucket = self._make_one(client, BUCKET_NAME, user_project=USER_PROJECT) - - with self.assertRaises(ValueError): - bucket.create() - def test_create_hit(self): - PROJECT = 'PROJECT' BUCKET_NAME = 'bucket-name' DATA = {'name': BUCKET_NAME} connection = _Connection(DATA) + PROJECT = 'PROJECT' client = _Client(connection, project=PROJECT) bucket = self._make_one(client=client, name=BUCKET_NAME) bucket.create() @@ -277,7 +176,6 @@ def test_create_w_extra_properties(self): 'location': LOCATION, 'storageClass': STORAGE_CLASS, 'versioning': {'enabled': True}, - 'billing': {'requesterPays': True}, 'labels': LABELS, } connection = _Connection(DATA) @@ -288,7 +186,6 @@ def test_create_w_extra_properties(self): bucket.location = LOCATION bucket.storage_class = STORAGE_CLASS bucket.versioning_enabled = True - bucket.requester_pays = True bucket.labels = LABELS bucket.create() @@ -335,20 +232,18 @@ def test_get_blob_miss(self): self.assertEqual(kw['method'], 'GET') self.assertEqual(kw['path'], '/b/%s/o/%s' % (NAME, NONESUCH)) - def test_get_blob_hit_w_user_project(self): + def test_get_blob_hit(self): NAME = 'name' BLOB_NAME = 'blob-name' - USER_PROJECT = 'user-project-123' connection = _Connection({'name': BLOB_NAME}) client = _Client(connection) - bucket = self._make_one(name=NAME, user_project=USER_PROJECT) + bucket = self._make_one(name=NAME) blob = bucket.get_blob(BLOB_NAME, client=client) self.assertIs(blob.bucket, bucket) self.assertEqual(blob.name, BLOB_NAME) kw, = connection._requested self.assertEqual(kw['method'], 'GET') self.assertEqual(kw['path'], '/b/%s/o/%s' % (NAME, BLOB_NAME)) - self.assertEqual(kw['query_params'], {'userProject': USER_PROJECT}) def test_get_blob_hit_with_kwargs(self): from google.cloud.storage.blob import _get_encryption_headers @@ -386,9 +281,8 @@ def test_list_blobs_defaults(self): self.assertEqual(kw['path'], '/b/%s/o' % NAME) self.assertEqual(kw['query_params'], {'projection': 'noAcl'}) - def test_list_blobs_w_all_arguments_and_user_project(self): + def test_list_blobs_w_all_arguments(self): NAME = 'name' - USER_PROJECT = 'user-project-123' MAX_RESULTS = 10 PAGE_TOKEN = 'ABCD' PREFIX = 'subfolder' @@ -404,11 +298,10 @@ def test_list_blobs_w_all_arguments_and_user_project(self): 'versions': VERSIONS, 'projection': PROJECTION, 'fields': FIELDS, - 'userProject': USER_PROJECT, } connection = _Connection({'items': []}) client = _Client(connection) - bucket = self._make_one(name=NAME, user_project=USER_PROJECT) + bucket = self._make_one(name=NAME) iterator = bucket.list_blobs( max_results=MAX_RESULTS, page_token=PAGE_TOKEN, @@ -439,58 +332,6 @@ def test_list_blobs(self): self.assertEqual(kw['path'], '/b/%s/o' % NAME) self.assertEqual(kw['query_params'], {'projection': 'noAcl'}) - def test_list_notifications(self): - from google.cloud.storage.notification import BucketNotification - from google.cloud.storage.notification import _TOPIC_REF_FMT - from google.cloud.storage.notification import ( - JSON_API_V1_PAYLOAD_FORMAT, NONE_PAYLOAD_FORMAT) - - NAME = 'name' - - topic_refs = [ - ('my-project-123', 'topic-1'), - ('other-project-456', 'topic-2'), - ] - - resources = [{ - 'topic': _TOPIC_REF_FMT.format(*topic_refs[0]), - 'id': '1', - 'etag': 'DEADBEEF', - 'selfLink': 'https://example.com/notification/1', - 'payload_format': NONE_PAYLOAD_FORMAT, - }, { - 'topic': _TOPIC_REF_FMT.format(*topic_refs[1]), - 'id': '2', - 'etag': 'FACECABB', - 'selfLink': 'https://example.com/notification/2', - 'payload_format': JSON_API_V1_PAYLOAD_FORMAT, - }] - connection = _Connection({'items': resources}) - client = _Client(connection) - bucket = self._make_one(client=client, name=NAME) - - notifications = list(bucket.list_notifications()) - - self.assertEqual(len(notifications), len(resources)) - for notification, resource, topic_ref in zip( - notifications, resources, topic_refs): - self.assertIsInstance(notification, BucketNotification) - self.assertEqual(notification.topic_project, topic_ref[0]) - self.assertEqual(notification.topic_name, topic_ref[1]) - self.assertEqual(notification.notification_id, resource['id']) - self.assertEqual(notification.etag, resource['etag']) - self.assertEqual(notification.self_link, resource['selfLink']) - self.assertEqual( - notification.custom_attributes, - resource.get('custom_attributes')) - self.assertEqual( - notification.event_types, resource.get('event_types')) - self.assertEqual( - notification.blob_name_prefix, - resource.get('blob_name_prefix')) - self.assertEqual( - notification.payload_format, resource.get('payload_format')) - def test_delete_miss(self): from google.cloud.exceptions import NotFound @@ -502,27 +343,23 @@ def test_delete_miss(self): expected_cw = [{ 'method': 'DELETE', 'path': bucket.path, - 'query_params': {}, '_target_object': None, }] self.assertEqual(connection._deleted_buckets, expected_cw) - def test_delete_hit_with_user_project(self): + def test_delete_hit(self): NAME = 'name' - USER_PROJECT = 'user-project-123' GET_BLOBS_RESP = {'items': []} connection = _Connection(GET_BLOBS_RESP) connection._delete_bucket = True client = _Client(connection) - bucket = self._make_one( - client=client, name=NAME, user_project=USER_PROJECT) + bucket = self._make_one(client=client, name=NAME) result = bucket.delete(force=True) self.assertIsNone(result) expected_cw = [{ 'method': 'DELETE', 'path': bucket.path, '_target_object': None, - 'query_params': {'userProject': USER_PROJECT}, }] self.assertEqual(connection._deleted_buckets, expected_cw) @@ -547,7 +384,6 @@ def test_delete_force_delete_blobs(self): expected_cw = [{ 'method': 'DELETE', 'path': bucket.path, - 'query_params': {}, '_target_object': None, }] self.assertEqual(connection._deleted_buckets, expected_cw) @@ -566,7 +402,6 @@ def test_delete_force_miss_blobs(self): expected_cw = [{ 'method': 'DELETE', 'path': bucket.path, - 'query_params': {}, '_target_object': None, }] self.assertEqual(connection._deleted_buckets, expected_cw) @@ -603,22 +438,18 @@ def test_delete_blob_miss(self): kw, = connection._requested self.assertEqual(kw['method'], 'DELETE') self.assertEqual(kw['path'], '/b/%s/o/%s' % (NAME, NONESUCH)) - self.assertEqual(kw['query_params'], {}) - def test_delete_blob_hit_with_user_project(self): + def test_delete_blob_hit(self): NAME = 'name' BLOB_NAME = 'blob-name' - USER_PROJECT = 'user-project-123' connection = _Connection({}) client = _Client(connection) - bucket = self._make_one( - client=client, name=NAME, user_project=USER_PROJECT) + bucket = self._make_one(client=client, name=NAME) result = bucket.delete_blob(BLOB_NAME) self.assertIsNone(result) kw, = connection._requested self.assertEqual(kw['method'], 'DELETE') self.assertEqual(kw['path'], '/b/%s/o/%s' % (NAME, BLOB_NAME)) - self.assertEqual(kw['query_params'], {'userProject': USER_PROJECT}) def test_delete_blobs_empty(self): NAME = 'name' @@ -628,20 +459,17 @@ def test_delete_blobs_empty(self): bucket.delete_blobs([]) self.assertEqual(connection._requested, []) - def test_delete_blobs_hit_w_user_project(self): + def test_delete_blobs_hit(self): NAME = 'name' BLOB_NAME = 'blob-name' - USER_PROJECT = 'user-project-123' connection = _Connection({}) client = _Client(connection) - bucket = self._make_one( - client=client, name=NAME, user_project=USER_PROJECT) + bucket = self._make_one(client=client, name=NAME) bucket.delete_blobs([BLOB_NAME]) kw = connection._requested self.assertEqual(len(kw), 1) self.assertEqual(kw[0]['method'], 'DELETE') self.assertEqual(kw[0]['path'], '/b/%s/o/%s' % (NAME, BLOB_NAME)) - self.assertEqual(kw[0]['query_params'], {'userProject': USER_PROJECT}) def test_delete_blobs_miss_no_on_error(self): from google.cloud.exceptions import NotFound @@ -699,7 +527,6 @@ class _Blob(object): DEST, BLOB_NAME) self.assertEqual(kw['method'], 'POST') self.assertEqual(kw['path'], COPY_PATH) - self.assertEqual(kw['query_params'], {}) def test_copy_blobs_preserve_acl(self): from google.cloud.storage.acl import ObjectACL @@ -731,17 +558,14 @@ class _Blob(object): self.assertEqual(len(kw), 2) self.assertEqual(kw[0]['method'], 'POST') self.assertEqual(kw[0]['path'], COPY_PATH) - self.assertEqual(kw[0]['query_params'], {}) self.assertEqual(kw[1]['method'], 'PATCH') self.assertEqual(kw[1]['path'], NEW_BLOB_PATH) - self.assertEqual(kw[1]['query_params'], {'projection': 'full'}) - def test_copy_blobs_w_name_and_user_project(self): + def test_copy_blobs_w_name(self): SOURCE = 'source' DEST = 'dest' BLOB_NAME = 'blob-name' NEW_NAME = 'new_name' - USER_PROJECT = 'user-project-123' class _Blob(object): name = BLOB_NAME @@ -749,8 +573,7 @@ class _Blob(object): connection = _Connection({}) client = _Client(connection) - source = self._make_one( - client=client, name=SOURCE, user_project=USER_PROJECT) + source = self._make_one(client=client, name=SOURCE) dest = self._make_one(client=client, name=DEST) blob = _Blob() new_blob = source.copy_blob(blob, dest, NEW_NAME) @@ -761,7 +584,6 @@ class _Blob(object): DEST, NEW_NAME) self.assertEqual(kw['method'], 'POST') self.assertEqual(kw['path'], COPY_PATH) - self.assertEqual(kw['query_params'], {'userProject': USER_PROJECT}) def test_rename_blob(self): BUCKET_NAME = 'BUCKET_NAME' @@ -1094,24 +916,6 @@ def test_versioning_enabled_setter(self): bucket.versioning_enabled = True self.assertTrue(bucket.versioning_enabled) - def test_requester_pays_getter_missing(self): - NAME = 'name' - bucket = self._make_one(name=NAME) - self.assertEqual(bucket.requester_pays, False) - - def test_requester_pays_getter(self): - NAME = 'name' - before = {'billing': {'requesterPays': True}} - bucket = self._make_one(name=NAME, properties=before) - self.assertEqual(bucket.requester_pays, True) - - def test_requester_pays_setter(self): - NAME = 'name' - bucket = self._make_one(name=NAME) - self.assertFalse(bucket.requester_pays) - bucket.requester_pays = True - self.assertTrue(bucket.requester_pays) - def test_configure_website_defaults(self): NAME = 'name' UNSET = {'website': {'mainPageSuffix': None, @@ -1180,40 +984,6 @@ def test_get_iam_policy(self): self.assertEqual(len(kw), 1) self.assertEqual(kw[0]['method'], 'GET') self.assertEqual(kw[0]['path'], '%s/iam' % (PATH,)) - self.assertEqual(kw[0]['query_params'], {}) - - def test_get_iam_policy_w_user_project(self): - from google.cloud.iam import Policy - - NAME = 'name' - USER_PROJECT = 'user-project-123' - PATH = '/b/%s' % (NAME,) - ETAG = 'DEADBEEF' - VERSION = 17 - RETURNED = { - 'resourceId': PATH, - 'etag': ETAG, - 'version': VERSION, - 'bindings': [], - } - EXPECTED = {} - connection = _Connection(RETURNED) - client = _Client(connection, None) - bucket = self._make_one( - client=client, name=NAME, user_project=USER_PROJECT) - - policy = bucket.get_iam_policy() - - self.assertIsInstance(policy, Policy) - self.assertEqual(policy.etag, RETURNED['etag']) - self.assertEqual(policy.version, RETURNED['version']) - self.assertEqual(dict(policy), EXPECTED) - - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]['method'], 'GET') - self.assertEqual(kw[0]['path'], '%s/iam' % (PATH,)) - self.assertEqual(kw[0]['query_params'], {'userProject': USER_PROJECT}) def test_set_iam_policy(self): import operator @@ -1260,66 +1030,6 @@ def test_set_iam_policy(self): self.assertEqual(len(kw), 1) self.assertEqual(kw[0]['method'], 'PUT') self.assertEqual(kw[0]['path'], '%s/iam' % (PATH,)) - self.assertEqual(kw[0]['query_params'], {}) - sent = kw[0]['data'] - self.assertEqual(sent['resourceId'], PATH) - self.assertEqual(len(sent['bindings']), len(BINDINGS)) - key = operator.itemgetter('role') - for found, expected in zip( - sorted(sent['bindings'], key=key), - sorted(BINDINGS, key=key)): - self.assertEqual(found['role'], expected['role']) - self.assertEqual( - sorted(found['members']), sorted(expected['members'])) - - def test_set_iam_policy_w_user_project(self): - import operator - from google.cloud.storage.iam import STORAGE_OWNER_ROLE - from google.cloud.storage.iam import STORAGE_EDITOR_ROLE - from google.cloud.storage.iam import STORAGE_VIEWER_ROLE - from google.cloud.iam import Policy - - NAME = 'name' - USER_PROJECT = 'user-project-123' - PATH = '/b/%s' % (NAME,) - ETAG = 'DEADBEEF' - VERSION = 17 - OWNER1 = 'user:phred@example.com' - OWNER2 = 'group:cloud-logs@google.com' - EDITOR1 = 'domain:google.com' - EDITOR2 = 'user:phred@example.com' - VIEWER1 = 'serviceAccount:1234-abcdef@service.example.com' - VIEWER2 = 'user:phred@example.com' - BINDINGS = [ - {'role': STORAGE_OWNER_ROLE, 'members': [OWNER1, OWNER2]}, - {'role': STORAGE_EDITOR_ROLE, 'members': [EDITOR1, EDITOR2]}, - {'role': STORAGE_VIEWER_ROLE, 'members': [VIEWER1, VIEWER2]}, - ] - RETURNED = { - 'etag': ETAG, - 'version': VERSION, - 'bindings': BINDINGS, - } - policy = Policy() - for binding in BINDINGS: - policy[binding['role']] = binding['members'] - - connection = _Connection(RETURNED) - client = _Client(connection, None) - bucket = self._make_one( - client=client, name=NAME, user_project=USER_PROJECT) - - returned = bucket.set_iam_policy(policy) - - self.assertEqual(returned.etag, ETAG) - self.assertEqual(returned.version, VERSION) - self.assertEqual(dict(returned), dict(policy)) - - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]['method'], 'PUT') - self.assertEqual(kw[0]['path'], '%s/iam' % (PATH,)) - self.assertEqual(kw[0]['query_params'], {'userProject': USER_PROJECT}) sent = kw[0]['data'] self.assertEqual(sent['resourceId'], PATH) self.assertEqual(len(sent['bindings']), len(BINDINGS)) @@ -1359,38 +1069,6 @@ def test_test_iam_permissions(self): self.assertEqual(kw[0]['path'], '%s/iam/testPermissions' % (PATH,)) self.assertEqual(kw[0]['query_params'], {'permissions': PERMISSIONS}) - def test_test_iam_permissions_w_user_project(self): - from google.cloud.storage.iam import STORAGE_OBJECTS_LIST - from google.cloud.storage.iam import STORAGE_BUCKETS_GET - from google.cloud.storage.iam import STORAGE_BUCKETS_UPDATE - - NAME = 'name' - USER_PROJECT = 'user-project-123' - PATH = '/b/%s' % (NAME,) - PERMISSIONS = [ - STORAGE_OBJECTS_LIST, - STORAGE_BUCKETS_GET, - STORAGE_BUCKETS_UPDATE, - ] - ALLOWED = PERMISSIONS[1:] - RETURNED = {'permissions': ALLOWED} - connection = _Connection(RETURNED) - client = _Client(connection, None) - bucket = self._make_one( - client=client, name=NAME, user_project=USER_PROJECT) - - allowed = bucket.test_iam_permissions(PERMISSIONS) - - self.assertEqual(allowed, ALLOWED) - - kw = connection._requested - self.assertEqual(len(kw), 1) - self.assertEqual(kw[0]['method'], 'GET') - self.assertEqual(kw[0]['path'], '%s/iam/testPermissions' % (PATH,)) - self.assertEqual( - kw[0]['query_params'], - {'permissions': PERMISSIONS, 'userProject': USER_PROJECT}) - def test_make_public_defaults(self): from google.cloud.storage.acl import _ACLEntity diff --git a/storage/tests/unit/test_client.py b/storage/tests/unit/test_client.py index cb3da57117ad..39a27b9c0773 100644 --- a/storage/tests/unit/test_client.py +++ b/storage/tests/unit/test_client.py @@ -140,22 +140,6 @@ def test_bucket(self): self.assertIsInstance(bucket, Bucket) self.assertIs(bucket.client, client) self.assertEqual(bucket.name, BUCKET_NAME) - self.assertIsNone(bucket.user_project) - - def test_bucket_w_user_project(self): - from google.cloud.storage.bucket import Bucket - - PROJECT = 'PROJECT' - USER_PROJECT = 'USER_PROJECT' - CREDENTIALS = _make_credentials() - BUCKET_NAME = 'BUCKET_NAME' - - client = self._make_one(project=PROJECT, credentials=CREDENTIALS) - bucket = client.bucket(BUCKET_NAME, user_project=USER_PROJECT) - self.assertIsInstance(bucket, Bucket) - self.assertIs(bucket.client, client) - self.assertEqual(bucket.name, BUCKET_NAME) - self.assertEqual(bucket.user_project, USER_PROJECT) def test_batch(self): from google.cloud.storage.batch import Batch @@ -200,23 +184,23 @@ def test_get_bucket_hit(self): CREDENTIALS = _make_credentials() client = self._make_one(project=PROJECT, credentials=CREDENTIALS) - BUCKET_NAME = 'bucket-name' + BLOB_NAME = 'blob-name' URI = '/'.join([ client._connection.API_BASE_URL, 'storage', client._connection.API_VERSION, 'b', - '%s?projection=noAcl' % (BUCKET_NAME,), + '%s?projection=noAcl' % (BLOB_NAME,), ]) - data = {'name': BUCKET_NAME} + data = {'name': BLOB_NAME} http = _make_requests_session([_make_json_response(data)]) client._http_internal = http - bucket = client.get_bucket(BUCKET_NAME) + bucket = client.get_bucket(BLOB_NAME) self.assertIsInstance(bucket, Bucket) - self.assertEqual(bucket.name, BUCKET_NAME) + self.assertEqual(bucket.name, BLOB_NAME) http.request.assert_called_once_with( method='GET', url=URI, data=mock.ANY, headers=mock.ANY) @@ -250,22 +234,22 @@ def test_lookup_bucket_hit(self): CREDENTIALS = _make_credentials() client = self._make_one(project=PROJECT, credentials=CREDENTIALS) - BUCKET_NAME = 'bucket-name' + BLOB_NAME = 'blob-name' URI = '/'.join([ client._connection.API_BASE_URL, 'storage', client._connection.API_VERSION, 'b', - '%s?projection=noAcl' % (BUCKET_NAME,), + '%s?projection=noAcl' % (BLOB_NAME,), ]) - data = {'name': BUCKET_NAME} + data = {'name': BLOB_NAME} http = _make_requests_session([_make_json_response(data)]) client._http_internal = http - bucket = client.lookup_bucket(BUCKET_NAME) + bucket = client.lookup_bucket(BLOB_NAME) self.assertIsInstance(bucket, Bucket) - self.assertEqual(bucket.name, BUCKET_NAME) + self.assertEqual(bucket.name, BLOB_NAME) http.request.assert_called_once_with( method='GET', url=URI, data=mock.ANY, headers=mock.ANY) @@ -276,7 +260,7 @@ def test_create_bucket_conflict(self): CREDENTIALS = _make_credentials() client = self._make_one(project=PROJECT, credentials=CREDENTIALS) - BUCKET_NAME = 'bucket-name' + BLOB_NAME = 'blob-name' URI = '/'.join([ client._connection.API_BASE_URL, 'storage', @@ -284,16 +268,13 @@ def test_create_bucket_conflict(self): 'b?project=%s' % (PROJECT,), ]) data = {'error': {'message': 'Conflict'}} - sent = {'name': BUCKET_NAME} http = _make_requests_session([ _make_json_response(data, status=http_client.CONFLICT)]) client._http_internal = http - self.assertRaises(Conflict, client.create_bucket, BUCKET_NAME) + self.assertRaises(Conflict, client.create_bucket, BLOB_NAME) http.request.assert_called_once_with( method='POST', url=URI, data=mock.ANY, headers=mock.ANY) - json_sent = http.request.call_args_list[0][1]['data'] - self.assertEqual(sent, json.loads(json_sent)) def test_create_bucket_success(self): from google.cloud.storage.bucket import Bucket @@ -302,27 +283,23 @@ def test_create_bucket_success(self): CREDENTIALS = _make_credentials() client = self._make_one(project=PROJECT, credentials=CREDENTIALS) - BUCKET_NAME = 'bucket-name' + BLOB_NAME = 'blob-name' URI = '/'.join([ client._connection.API_BASE_URL, 'storage', client._connection.API_VERSION, 'b?project=%s' % (PROJECT,), ]) - sent = {'name': BUCKET_NAME, 'billing': {'requesterPays': True}} - data = sent + data = {'name': BLOB_NAME} http = _make_requests_session([_make_json_response(data)]) client._http_internal = http - bucket = client.create_bucket(BUCKET_NAME, requester_pays=True) + bucket = client.create_bucket(BLOB_NAME) self.assertIsInstance(bucket, Bucket) - self.assertEqual(bucket.name, BUCKET_NAME) - self.assertTrue(bucket.requester_pays) + self.assertEqual(bucket.name, BLOB_NAME) http.request.assert_called_once_with( method='POST', url=URI, data=mock.ANY, headers=mock.ANY) - json_sent = http.request.call_args_list[0][1]['data'] - self.assertEqual(sent, json.loads(json_sent)) def test_list_buckets_empty(self): from six.moves.urllib.parse import parse_qs @@ -445,7 +422,7 @@ def test_page_non_empty_response(self): credentials = _make_credentials() client = self._make_one(project=project, credentials=credentials) - blob_name = 'bucket-name' + blob_name = 'blob-name' response = {'items': [{'name': blob_name}]} def dummy_response(): diff --git a/storage/tests/unit/test_notification.py b/storage/tests/unit/test_notification.py deleted file mode 100644 index 59b8ca4ff70f..000000000000 --- a/storage/tests/unit/test_notification.py +++ /dev/null @@ -1,482 +0,0 @@ -# Copyright 2017 Google Inc. -# -# 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. - -import unittest - -import mock - - -class TestBucketNotification(unittest.TestCase): - - BUCKET_NAME = 'test-bucket' - BUCKET_PROJECT = 'bucket-project-123' - TOPIC_NAME = 'test-topic' - TOPIC_ALT_PROJECT = 'topic-project-456' - TOPIC_REF_FMT = '//pubsub.googleapis.com/projects/{}/topics/{}' - TOPIC_REF = TOPIC_REF_FMT.format(BUCKET_PROJECT, TOPIC_NAME) - TOPIC_ALT_REF = TOPIC_REF_FMT.format(TOPIC_ALT_PROJECT, TOPIC_NAME) - CUSTOM_ATTRIBUTES = { - 'attr1': 'value1', - 'attr2': 'value2', - } - BLOB_NAME_PREFIX = 'blob-name-prefix/' - NOTIFICATION_ID = '123' - SELF_LINK = 'https://example.com/notification/123' - ETAG = 'DEADBEEF' - CREATE_PATH = '/b/{}/notificationConfigs'.format(BUCKET_NAME) - NOTIFICATION_PATH = '/b/{}/notificationConfigs/{}'.format( - BUCKET_NAME, NOTIFICATION_ID) - - @staticmethod - def event_types(): - from google.cloud.storage.notification import ( - OBJECT_FINALIZE_EVENT_TYPE, - OBJECT_DELETE_EVENT_TYPE) - - return [OBJECT_FINALIZE_EVENT_TYPE, OBJECT_DELETE_EVENT_TYPE] - - @staticmethod - def payload_format(): - from google.cloud.storage.notification import ( - JSON_API_V1_PAYLOAD_FORMAT) - - return JSON_API_V1_PAYLOAD_FORMAT - - @staticmethod - def _get_target_class(): - from google.cloud.storage.notification import BucketNotification - - return BucketNotification - - def _make_one(self, *args, **kw): - return self._get_target_class()(*args, **kw) - - def _make_client(self, project=BUCKET_PROJECT): - from google.cloud.storage.client import Client - - return mock.Mock(project=project, spec=Client) - - def _make_bucket(self, client, name=BUCKET_NAME, user_project=None): - bucket = mock.Mock(spec=['client', 'name', 'user_project']) - bucket.client= client - bucket.name = name - bucket.user_project = user_project - return bucket - - def test_ctor_defaults(self): - from google.cloud.storage.notification import NONE_PAYLOAD_FORMAT - - client = self._make_client() - bucket = self._make_bucket(client) - - notification = self._make_one( - bucket, self.TOPIC_NAME) - - self.assertIs(notification.bucket, bucket) - self.assertEqual(notification.topic_name, self.TOPIC_NAME) - self.assertEqual(notification.topic_project, self.BUCKET_PROJECT) - self.assertIsNone(notification.custom_attributes) - self.assertIsNone(notification.event_types) - self.assertIsNone(notification.blob_name_prefix) - self.assertEqual(notification.payload_format, NONE_PAYLOAD_FORMAT) - - def test_ctor_explicit(self): - client = self._make_client() - bucket = self._make_bucket(client) - - notification = self._make_one( - bucket, self.TOPIC_NAME, - topic_project=self.TOPIC_ALT_PROJECT, - custom_attributes=self.CUSTOM_ATTRIBUTES, - event_types=self.event_types(), - blob_name_prefix=self.BLOB_NAME_PREFIX, - payload_format=self.payload_format(), - ) - - self.assertIs(notification.bucket, bucket) - self.assertEqual(notification.topic_name, self.TOPIC_NAME) - self.assertEqual(notification.topic_project, self.TOPIC_ALT_PROJECT) - self.assertEqual( - notification.custom_attributes, self.CUSTOM_ATTRIBUTES) - self.assertEqual(notification.event_types, self.event_types()) - self.assertEqual(notification.blob_name_prefix, self.BLOB_NAME_PREFIX) - self.assertEqual( - notification.payload_format, self.payload_format()) - - def test_from_api_repr_no_topic(self): - klass = self._get_target_class() - client = self._make_client() - bucket = self._make_bucket(client) - resource = {} - - with self.assertRaises(ValueError): - klass.from_api_repr(resource, bucket=bucket) - - def test_from_api_repr_invalid_topic(self): - klass = self._get_target_class() - client = self._make_client() - bucket = self._make_bucket(client) - resource = { - 'topic': '@#$%', - } - - with self.assertRaises(ValueError): - klass.from_api_repr(resource, bucket=bucket) - - def test_from_api_repr_minimal(self): - from google.cloud.storage.notification import NONE_PAYLOAD_FORMAT - - klass = self._get_target_class() - client = self._make_client() - bucket = self._make_bucket(client) - resource = { - 'topic': self.TOPIC_REF, - 'id': self.NOTIFICATION_ID, - 'etag': self.ETAG, - 'selfLink': self.SELF_LINK, - 'payload_format': NONE_PAYLOAD_FORMAT, - } - - notification = klass.from_api_repr(resource, bucket=bucket) - - self.assertIs(notification.bucket, bucket) - self.assertEqual(notification.topic_name, self.TOPIC_NAME) - self.assertEqual(notification.topic_project, self.BUCKET_PROJECT) - self.assertIsNone(notification.custom_attributes) - self.assertIsNone(notification.event_types) - self.assertIsNone(notification.blob_name_prefix) - self.assertEqual(notification.payload_format, NONE_PAYLOAD_FORMAT) - self.assertEqual(notification.etag, self.ETAG) - self.assertEqual(notification.self_link, self.SELF_LINK) - - def test_from_api_repr_explicit(self): - klass = self._get_target_class() - client = self._make_client() - bucket = self._make_bucket(client) - resource = { - 'topic': self.TOPIC_ALT_REF, - 'custom_attributes': self.CUSTOM_ATTRIBUTES, - 'event_types': self.event_types(), - 'object_name_prefix': self.BLOB_NAME_PREFIX, - 'payload_format': self.payload_format(), - 'id': self.NOTIFICATION_ID, - 'etag': self.ETAG, - 'selfLink': self.SELF_LINK, - } - - notification = klass.from_api_repr(resource, bucket=bucket) - - self.assertIs(notification.bucket, bucket) - self.assertEqual(notification.topic_name, self.TOPIC_NAME) - self.assertEqual(notification.topic_project, self.TOPIC_ALT_PROJECT) - self.assertEqual( - notification.custom_attributes, self.CUSTOM_ATTRIBUTES) - self.assertEqual(notification.event_types, self.event_types()) - self.assertEqual(notification.blob_name_prefix, self.BLOB_NAME_PREFIX) - self.assertEqual( - notification.payload_format, self.payload_format()) - self.assertEqual(notification.notification_id, self.NOTIFICATION_ID) - self.assertEqual(notification.etag, self.ETAG) - self.assertEqual(notification.self_link, self.SELF_LINK) - - def test_notification_id(self): - client = self._make_client() - bucket = self._make_bucket(client) - - notification = self._make_one( - bucket, self.TOPIC_NAME) - - self.assertIsNone(notification.notification_id) - - notification._properties['id'] = self.NOTIFICATION_ID - self.assertEqual(notification.notification_id, self.NOTIFICATION_ID) - - def test_etag(self): - client = self._make_client() - bucket = self._make_bucket(client) - - notification = self._make_one( - bucket, self.TOPIC_NAME) - - self.assertIsNone(notification.etag) - - notification._properties['etag'] = self.ETAG - self.assertEqual(notification.etag, self.ETAG) - - def test_self_link(self): - client = self._make_client() - bucket = self._make_bucket(client) - - notification = self._make_one( - bucket, self.TOPIC_NAME) - - self.assertIsNone(notification.self_link) - - notification._properties['selfLink'] = self.SELF_LINK - self.assertEqual(notification.self_link, self.SELF_LINK) - - def test_create_w_existing_notification_id(self): - client = self._make_client() - bucket = self._make_bucket(client) - notification = self._make_one( - bucket, self.TOPIC_NAME) - notification._properties['id'] = self.NOTIFICATION_ID - - with self.assertRaises(ValueError): - notification.create() - - def test_create_w_defaults(self): - from google.cloud.storage.notification import NONE_PAYLOAD_FORMAT - - client = self._make_client() - bucket = self._make_bucket(client) - notification = self._make_one( - bucket, self.TOPIC_NAME) - api_request = client._connection.api_request - api_request.return_value = { - 'topic': self.TOPIC_REF, - 'id': self.NOTIFICATION_ID, - 'etag': self.ETAG, - 'selfLink': self.SELF_LINK, - 'payload_format': NONE_PAYLOAD_FORMAT, - } - - notification.create() - - self.assertEqual(notification.notification_id, self.NOTIFICATION_ID) - self.assertEqual(notification.etag, self.ETAG) - self.assertEqual(notification.self_link, self.SELF_LINK) - self.assertIsNone(notification.custom_attributes) - self.assertIsNone(notification.event_types) - self.assertIsNone(notification.blob_name_prefix) - self.assertEqual(notification.payload_format, NONE_PAYLOAD_FORMAT) - - data = { - 'topic': self.TOPIC_REF, - 'payload_format': NONE_PAYLOAD_FORMAT, - } - api_request.assert_called_once_with( - method='POST', - path=self.CREATE_PATH, - query_params={}, - data=data, - ) - - def test_create_w_explicit_client(self): - USER_PROJECT = 'user-project-123' - client = self._make_client() - alt_client = self._make_client() - bucket = self._make_bucket(client, user_project=USER_PROJECT) - notification = self._make_one( - bucket, self.TOPIC_NAME, - topic_project=self.TOPIC_ALT_PROJECT, - custom_attributes=self.CUSTOM_ATTRIBUTES, - event_types=self.event_types(), - blob_name_prefix=self.BLOB_NAME_PREFIX, - payload_format=self.payload_format(), - ) - api_request = alt_client._connection.api_request - api_request.return_value = { - 'topic': self.TOPIC_ALT_REF, - 'custom_attributes': self.CUSTOM_ATTRIBUTES, - 'event_types': self.event_types(), - 'object_name_prefix': self.BLOB_NAME_PREFIX, - 'payload_format': self.payload_format(), - 'id': self.NOTIFICATION_ID, - 'etag': self.ETAG, - 'selfLink': self.SELF_LINK, - } - - notification.create(client=alt_client) - - self.assertEqual( - notification.custom_attributes, self.CUSTOM_ATTRIBUTES) - self.assertEqual(notification.event_types, self.event_types()) - self.assertEqual(notification.blob_name_prefix, self.BLOB_NAME_PREFIX) - self.assertEqual( - notification.payload_format, self.payload_format()) - self.assertEqual(notification.notification_id, self.NOTIFICATION_ID) - self.assertEqual(notification.etag, self.ETAG) - self.assertEqual(notification.self_link, self.SELF_LINK) - - data = { - 'topic': self.TOPIC_ALT_REF, - 'custom_attributes': self.CUSTOM_ATTRIBUTES, - 'event_types': self.event_types(), - 'object_name_prefix': self.BLOB_NAME_PREFIX, - 'payload_format': self.payload_format(), - } - api_request.assert_called_once_with( - method='POST', - path=self.CREATE_PATH, - query_params={'userProject': USER_PROJECT}, - data=data, - ) - - def test_exists_wo_notification_id(self): - client = self._make_client() - bucket = self._make_bucket(client) - notification = self._make_one( - bucket, self.TOPIC_NAME) - - with self.assertRaises(ValueError): - notification.exists() - - def test_exists_miss(self): - from google.cloud.exceptions import NotFound - - client = self._make_client() - bucket = self._make_bucket(client) - notification = self._make_one(bucket, self.TOPIC_NAME) - notification._properties['id'] = self.NOTIFICATION_ID - api_request = client._connection.api_request - api_request.side_effect = NotFound('testing') - - self.assertFalse(notification.exists()) - - api_request.assert_called_once_with( - method='GET', - path=self.NOTIFICATION_PATH, - query_params={}, - ) - - def test_exists_hit(self): - USER_PROJECT = 'user-project-123' - client = self._make_client() - bucket = self._make_bucket(client, user_project=USER_PROJECT) - alt_client = self._make_client() - notification = self._make_one(bucket, self.TOPIC_NAME) - notification._properties['id'] = self.NOTIFICATION_ID - api_request = client._connection.api_request - api_request.return_value = { - 'topic': self.TOPIC_REF, - 'id': self.NOTIFICATION_ID, - 'etag': self.ETAG, - 'selfLink': self.SELF_LINK, - } - - self.assertTrue(notification.exists(client=client)) - - api_request.assert_called_once_with( - method='GET', - path=self.NOTIFICATION_PATH, - query_params={'userProject': USER_PROJECT}, - ) - - def test_reload_wo_notification_id(self): - client = self._make_client() - bucket = self._make_bucket(client) - notification = self._make_one( - bucket, self.TOPIC_NAME) - - with self.assertRaises(ValueError): - notification.reload() - - def test_reload_miss(self): - from google.cloud.exceptions import NotFound - - client = self._make_client() - bucket = self._make_bucket(client) - notification = self._make_one(bucket, self.TOPIC_NAME) - notification._properties['id'] = self.NOTIFICATION_ID - api_request = client._connection.api_request - api_request.side_effect = NotFound('testing') - - with self.assertRaises(NotFound): - notification.reload() - - api_request.assert_called_once_with( - method='GET', - path=self.NOTIFICATION_PATH, - query_params={}, - ) - - def test_reload_hit(self): - from google.cloud.storage.notification import NONE_PAYLOAD_FORMAT - - USER_PROJECT = 'user-project-123' - client = self._make_client() - bucket = self._make_bucket(client, user_project=USER_PROJECT) - alt_client = self._make_client() - notification = self._make_one(bucket, self.TOPIC_NAME) - notification._properties['id'] = self.NOTIFICATION_ID - api_request = client._connection.api_request - api_request.return_value = { - 'topic': self.TOPIC_REF, - 'id': self.NOTIFICATION_ID, - 'etag': self.ETAG, - 'selfLink': self.SELF_LINK, - 'payload_format': NONE_PAYLOAD_FORMAT, - } - - notification.reload(client=client) - - self.assertEqual(notification.etag, self.ETAG) - self.assertEqual(notification.self_link, self.SELF_LINK) - self.assertIsNone(notification.custom_attributes) - self.assertIsNone(notification.event_types) - self.assertIsNone(notification.blob_name_prefix) - self.assertEqual(notification.payload_format, NONE_PAYLOAD_FORMAT) - - api_request.assert_called_once_with( - method='GET', - path=self.NOTIFICATION_PATH, - query_params={'userProject': USER_PROJECT}, - ) - - def test_delete_wo_notification_id(self): - client = self._make_client() - bucket = self._make_bucket(client) - notification = self._make_one( - bucket, self.TOPIC_NAME) - - with self.assertRaises(ValueError): - notification.delete() - - def test_delete_miss(self): - from google.cloud.exceptions import NotFound - - client = self._make_client() - bucket = self._make_bucket(client) - notification = self._make_one(bucket, self.TOPIC_NAME) - notification._properties['id'] = self.NOTIFICATION_ID - api_request = client._connection.api_request - api_request.side_effect = NotFound('testing') - - with self.assertRaises(NotFound): - notification.delete() - - api_request.assert_called_once_with( - method='DELETE', - path=self.NOTIFICATION_PATH, - query_params={}, - ) - - def test_delete_hit(self): - USER_PROJECT = 'user-project-123' - client = self._make_client() - bucket = self._make_bucket(client, user_project=USER_PROJECT) - alt_client = self._make_client() - notification = self._make_one(bucket, self.TOPIC_NAME) - notification._properties['id'] = self.NOTIFICATION_ID - api_request = client._connection.api_request - api_request.return_value = None - - notification.delete(client=client) - - api_request.assert_called_once_with( - method='DELETE', - path=self.NOTIFICATION_PATH, - query_params={'userProject': USER_PROJECT}, - )