Skip to content

Commit 7190c86

Browse files
authored
Add 'BucketNotification.{reload,exists}' API wrappers. (#3986)
Toward #3956.
1 parent 0a2a40f commit 7190c86

2 files changed

Lines changed: 194 additions & 43 deletions

File tree

storage/google/cloud/storage/notification.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
"""Support for bucket notification resources."""
1616

17+
from google.api.core.exceptions import NotFound
18+
19+
1720
OBJECT_FINALIZE_EVENT_TYPE = 'OBJECT_FINALIZE'
1821
OBJECT_METADATA_UPDATE_EVENT_TYPE = 'OBJECT_METADATA_UPDATE'
1922
OBJECT_DELETE_EVENT_TYPE = 'OBJECT_DELETE'
@@ -161,6 +164,15 @@ def _require_client(self, client):
161164
client = self.client
162165
return client
163166

167+
def _set_properties(self, response):
168+
"""Helper for :meth:`reload`.
169+
170+
:type response: dict
171+
:param response: resource mapping from server
172+
"""
173+
self._properties.clear()
174+
self._properties.update(response)
175+
164176
def create(self, client=None):
165177
"""API wrapper: create the notification.
166178
@@ -187,6 +199,54 @@ def create(self, client=None):
187199
data=properties,
188200
)
189201

202+
def exists(self, client=None):
203+
"""Test whether this notification exists.
204+
205+
See:
206+
https://cloud.google.com/storage/docs/json_api/v1/notifications/get
207+
208+
:type client: :class:`~google.cloud.storage.client.Client` or
209+
``NoneType``
210+
:param client: Optional. The client to use. If not passed, falls back
211+
to the ``client`` stored on the current bucket.
212+
213+
:rtype: bool
214+
:returns: True, if the notification exists, else False.
215+
:raises ValueError: if the notification has no ID.
216+
"""
217+
if self.notification_id is None:
218+
raise ValueError("Notification not intialized by server")
219+
220+
client = self._require_client(client)
221+
try:
222+
client._connection.api_request(method='GET', path=self.path)
223+
except NotFound:
224+
return False
225+
else:
226+
return True
227+
228+
def reload(self, client=None):
229+
"""Update this notification from the server configuration.
230+
231+
See:
232+
https://cloud.google.com/storage/docs/json_api/v1/notifications/get
233+
234+
:type client: :class:`~google.cloud.storage.client.Client` or
235+
``NoneType``
236+
:param client: Optional. The client to use. If not passed, falls back
237+
to the ``client`` stored on the current bucket.
238+
239+
:rtype: bool
240+
:returns: True, if the notification exists, else False.
241+
:raises ValueError: if the notification has no ID.
242+
"""
243+
if self.notification_id is None:
244+
raise ValueError("Notification not intialized by server")
245+
246+
client = self._require_client(client)
247+
response = client._connection.api_request(method='GET', path=self.path)
248+
self._set_properties(response)
249+
190250
def delete(self, client=None):
191251
"""Delete this notification.
192252
@@ -200,6 +260,7 @@ def delete(self, client=None):
200260
201261
:raises: :class:`google.api.core.exceptions.NotFound`:
202262
if the notification does not exist.
263+
:raises ValueError: if the notification has no ID.
203264
"""
204265
if self.notification_id is None:
205266
raise ValueError("Notification not intialized by server")

storage/tests/unit/test_notification.py

Lines changed: 133 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ class TestBucketNotification(unittest.TestCase):
3131
'attr2': 'value2',
3232
}
3333
BLOB_NAME_PREFIX = 'blob-name-prefix/'
34+
NOTIFICATION_ID = '123'
35+
SELF_LINK = 'https://example.com/notification/123'
36+
ETAG = 'DEADBEEF'
37+
CREATE_PATH = '/b/{}/notificationConfigs'.format(BUCKET_NAME)
38+
NOTIFICATION_PATH = '/b/{}/notificationConfigs/{}'.format(
39+
BUCKET_NAME, NOTIFICATION_ID)
3440

3541
@staticmethod
3642
def event_types():
@@ -108,93 +114,82 @@ def test_ctor_explicit(self):
108114
def test_notification_id(self):
109115
client = self._make_client()
110116
bucket = self._make_bucket(client)
111-
NOTIFICATION_ID = '123'
112117

113118
notification = self._make_one(
114119
bucket, self.TOPIC_NAME)
115120

116121
self.assertIsNone(notification.notification_id)
117122

118-
notification._properties['id'] = NOTIFICATION_ID
119-
self.assertEqual(notification.notification_id, NOTIFICATION_ID)
123+
notification._properties['id'] = self.NOTIFICATION_ID
124+
self.assertEqual(notification.notification_id, self.NOTIFICATION_ID)
120125

121126
def test_etag(self):
122127
client = self._make_client()
123128
bucket = self._make_bucket(client)
124-
ETAG = 'DEADBEEF'
125129

126130
notification = self._make_one(
127131
bucket, self.TOPIC_NAME)
128132

129133
self.assertIsNone(notification.etag)
130134

131-
notification._properties['etag'] = ETAG
132-
self.assertEqual(notification.etag, ETAG)
135+
notification._properties['etag'] = self.ETAG
136+
self.assertEqual(notification.etag, self.ETAG)
133137

134138
def test_self_link(self):
135139
client = self._make_client()
136140
bucket = self._make_bucket(client)
137-
SELF_LINK = 'https://example.com/notification/123'
138141

139142
notification = self._make_one(
140143
bucket, self.TOPIC_NAME)
141144

142145
self.assertIsNone(notification.self_link)
143146

144-
notification._properties['selfLink'] = SELF_LINK
145-
self.assertEqual(notification.self_link, SELF_LINK)
147+
notification._properties['selfLink'] = self.SELF_LINK
148+
self.assertEqual(notification.self_link, self.SELF_LINK)
146149

147150
def test_create_w_existing_notification_id(self):
148-
NOTIFICATION_ID = '123'
149151
client = self._make_client()
150152
bucket = self._make_bucket(client)
151153
notification = self._make_one(
152154
bucket, self.TOPIC_NAME)
153-
notification._properties['id'] = NOTIFICATION_ID
155+
notification._properties['id'] = self.NOTIFICATION_ID
154156

155157
with self.assertRaises(ValueError):
156158
notification.create()
157159

158160
def test_create_w_defaults(self):
159-
NOTIFICATION_ID = '123'
160-
ETAG = 'DEADBEEF'
161-
SELF_LINK = 'https://example.com/notification/123'
162161
client = self._make_client()
163162
bucket = self._make_bucket(client)
164163
notification = self._make_one(
165164
bucket, self.TOPIC_NAME)
166165
api_request = client._connection.api_request
167166
api_request.return_value = {
168167
'topic': self.TOPIC_REF,
169-
'id': NOTIFICATION_ID,
170-
'etag': ETAG,
171-
'selfLink': SELF_LINK,
168+
'id': self.NOTIFICATION_ID,
169+
'etag': self.ETAG,
170+
'selfLink': self.SELF_LINK,
172171
}
173172

174173
notification.create()
175174

176-
self.assertEqual(notification.notification_id, NOTIFICATION_ID)
177-
self.assertEqual(notification.etag, ETAG)
178-
self.assertEqual(notification.self_link, SELF_LINK)
175+
self.assertEqual(notification.notification_id, self.NOTIFICATION_ID)
176+
self.assertEqual(notification.etag, self.ETAG)
177+
self.assertEqual(notification.self_link, self.SELF_LINK)
179178
self.assertIsNone(notification.custom_attributes)
180179
self.assertIsNone(notification.event_types)
181180
self.assertIsNone(notification.blob_name_prefix)
182181
self.assertIsNone(notification.payload_format)
183182

184-
path = '/b/{}/notificationConfigs'.format(self.BUCKET_NAME)
185183
data = {
186184
'topic': self.TOPIC_REF,
187185
}
188186
api_request.assert_called_once_with(
189187
method='POST',
190-
path=path,
188+
path=self.CREATE_PATH,
191189
data=data,
192190
)
193191

194192
def test_create_w_explicit_client(self):
195-
NOTIFICATION_ID = '123'
196-
ETAG = 'DEADBEEF'
197-
SELF_LINK = 'https://example.com/notification/123'
198193
client = self._make_client()
199194
alt_client = self._make_client()
200195
bucket = self._make_bucket(client)
@@ -213,9 +208,9 @@ def test_create_w_explicit_client(self):
213208
'event_types': self.event_types(),
214209
'blob_name_prefix': self.BLOB_NAME_PREFIX,
215210
'payload_format': self.payload_format(),
216-
'id': NOTIFICATION_ID,
217-
'etag': ETAG,
218-
'selfLink': SELF_LINK,
211+
'id': self.NOTIFICATION_ID,
212+
'etag': self.ETAG,
213+
'selfLink': self.SELF_LINK,
219214
}
220215

221216
notification.create(client=alt_client)
@@ -226,11 +221,10 @@ def test_create_w_explicit_client(self):
226221
self.assertEqual(notification.blob_name_prefix, self.BLOB_NAME_PREFIX)
227222
self.assertEqual(
228223
notification.payload_format, self.payload_format())
229-
self.assertEqual(notification.notification_id, NOTIFICATION_ID)
230-
self.assertEqual(notification.etag, ETAG)
231-
self.assertEqual(notification.self_link, SELF_LINK)
224+
self.assertEqual(notification.notification_id, self.NOTIFICATION_ID)
225+
self.assertEqual(notification.etag, self.ETAG)
226+
self.assertEqual(notification.self_link, self.SELF_LINK)
232227

233-
path = '/b/{}/notificationConfigs'.format(self.BUCKET_NAME)
234228
data = {
235229
'topic': self.TOPIC_ALT_REF,
236230
'custom_attributes': self.CUSTOM_ATTRIBUTES,
@@ -240,10 +234,112 @@ def test_create_w_explicit_client(self):
240234
}
241235
api_request.assert_called_once_with(
242236
method='POST',
243-
path=path,
237+
path=self.CREATE_PATH,
244238
data=data,
245239
)
246240

241+
def test_exists_wo_notification_id(self):
242+
client = self._make_client()
243+
bucket = self._make_bucket(client)
244+
notification = self._make_one(
245+
bucket, self.TOPIC_NAME)
246+
247+
with self.assertRaises(ValueError):
248+
notification.exists()
249+
250+
def test_exists_miss(self):
251+
from google.cloud.exceptions import NotFound
252+
253+
client = self._make_client()
254+
bucket = self._make_bucket(client)
255+
notification = self._make_one(bucket, self.TOPIC_NAME)
256+
notification._properties['id'] = self.NOTIFICATION_ID
257+
api_request = client._connection.api_request
258+
api_request.side_effect = NotFound('testing')
259+
260+
self.assertFalse(notification.exists())
261+
262+
api_request.assert_called_once_with(
263+
method='GET',
264+
path=self.NOTIFICATION_PATH,
265+
)
266+
267+
def test_exists_hit(self):
268+
client = self._make_client()
269+
bucket = self._make_bucket(client)
270+
alt_client = self._make_client()
271+
notification = self._make_one(bucket, self.TOPIC_NAME)
272+
notification._properties['id'] = self.NOTIFICATION_ID
273+
api_request = client._connection.api_request
274+
api_request.return_value = {
275+
'topic': self.TOPIC_REF,
276+
'id': self.NOTIFICATION_ID,
277+
'etag': self.ETAG,
278+
'selfLink': self.SELF_LINK,
279+
}
280+
281+
self.assertTrue(notification.exists(client=client))
282+
283+
api_request.assert_called_once_with(
284+
method='GET',
285+
path=self.NOTIFICATION_PATH,
286+
)
287+
288+
def test_reload_wo_notification_id(self):
289+
client = self._make_client()
290+
bucket = self._make_bucket(client)
291+
notification = self._make_one(
292+
bucket, self.TOPIC_NAME)
293+
294+
with self.assertRaises(ValueError):
295+
notification.reload()
296+
297+
def test_reload_miss(self):
298+
from google.cloud.exceptions import NotFound
299+
300+
client = self._make_client()
301+
bucket = self._make_bucket(client)
302+
notification = self._make_one(bucket, self.TOPIC_NAME)
303+
notification._properties['id'] = self.NOTIFICATION_ID
304+
api_request = client._connection.api_request
305+
api_request.side_effect = NotFound('testing')
306+
307+
with self.assertRaises(NotFound):
308+
notification.reload()
309+
310+
api_request.assert_called_once_with(
311+
method='GET',
312+
path=self.NOTIFICATION_PATH,
313+
)
314+
315+
def test_reload_hit(self):
316+
client = self._make_client()
317+
bucket = self._make_bucket(client)
318+
alt_client = self._make_client()
319+
notification = self._make_one(bucket, self.TOPIC_NAME)
320+
notification._properties['id'] = self.NOTIFICATION_ID
321+
api_request = client._connection.api_request
322+
api_request.return_value = {
323+
'topic': self.TOPIC_REF,
324+
'id': self.NOTIFICATION_ID,
325+
'etag': self.ETAG,
326+
'selfLink': self.SELF_LINK,
327+
}
328+
329+
notification.reload(client=client)
330+
331+
self.assertEqual(notification.etag, self.ETAG)
332+
self.assertEqual(notification.self_link, self.SELF_LINK)
333+
self.assertIsNone(notification.custom_attributes)
334+
self.assertIsNone(notification.event_types)
335+
self.assertIsNone(notification.blob_name_prefix)
336+
self.assertIsNone(notification.payload_format)
337+
338+
api_request.assert_called_once_with(
339+
method='GET',
340+
path=self.NOTIFICATION_PATH,
341+
)
342+
247343
def test_delete_wo_notification_id(self):
248344
client = self._make_client()
249345
bucket = self._make_bucket(client)
@@ -256,39 +352,33 @@ def test_delete_wo_notification_id(self):
256352
def test_delete_miss(self):
257353
from google.cloud.exceptions import NotFound
258354

259-
NOTIFICATION_ID = '123'
260355
client = self._make_client()
261356
bucket = self._make_bucket(client)
262357
notification = self._make_one(bucket, self.TOPIC_NAME)
263-
notification._properties['id'] = NOTIFICATION_ID
358+
notification._properties['id'] = self.NOTIFICATION_ID
264359
api_request = client._connection.api_request
265360
api_request.side_effect = NotFound('testing')
266361

267362
with self.assertRaises(NotFound):
268363
notification.delete()
269364

270-
path = '/b/{}/notificationConfigs/{}'.format(
271-
self.BUCKET_NAME, NOTIFICATION_ID)
272365
api_request.assert_called_once_with(
273366
method='DELETE',
274-
path=path,
367+
path=self.NOTIFICATION_PATH,
275368
)
276369

277370
def test_delete_hit(self):
278-
NOTIFICATION_ID = '123'
279371
client = self._make_client()
280372
bucket = self._make_bucket(client)
281373
alt_client = self._make_client()
282374
notification = self._make_one(bucket, self.TOPIC_NAME)
283-
notification._properties['id'] = NOTIFICATION_ID
375+
notification._properties['id'] = self.NOTIFICATION_ID
284376
api_request = client._connection.api_request
285377
api_request.return_value = None
286378

287379
notification.delete(client=client)
288380

289-
path = '/b/{}/notificationConfigs/{}'.format(
290-
self.BUCKET_NAME, NOTIFICATION_ID)
291381
api_request.assert_called_once_with(
292382
method='DELETE',
293-
path=path,
383+
path=self.NOTIFICATION_PATH,
294384
)

0 commit comments

Comments
 (0)