Skip to content

Commit 3060050

Browse files
committed
Add blob properties to support retention policy feature. (#446)
Toward #445.
1 parent 33cd1b9 commit 3060050

File tree

2 files changed

+109
-0
lines changed

2 files changed

+109
-0
lines changed

storage/google/cloud/storage/blob.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,6 +1591,16 @@ def etag(self):
15911591
"""
15921592
return self._properties.get('etag')
15931593

1594+
event_based_hold = _scalar_property('eventBasedHold')
1595+
"""Is an event-based hold active on the object?
1596+
1597+
See `API reference docs`_.
1598+
1599+
If the property is not set locally, returns :data:`None`.
1600+
1601+
:rtype: bool or ``NoneType``
1602+
"""
1603+
15941604
@property
15951605
def generation(self):
15961606
"""Retrieve the generation for the object.
@@ -1697,6 +1707,20 @@ def owner(self):
16971707
"""
16981708
return copy.deepcopy(self._properties.get('owner'))
16991709

1710+
@property
1711+
def retention_expiration_time(self):
1712+
"""Retrieve timestamp at which the object's retention period expires.
1713+
1714+
See https://cloud.google.com/storage/docs/json_api/v1/objects
1715+
1716+
:rtype: :class:`datetime.datetime` or ``NoneType``
1717+
:returns: Datetime object parsed from RFC3339 valid timestamp, or
1718+
``None`` if the property is not set locally.
1719+
"""
1720+
value = self._properties.get('retentionExpirationTime')
1721+
if value is not None:
1722+
return _rfc3339_to_datetime(value)
1723+
17001724
@property
17011725
def self_link(self):
17021726
"""Retrieve the URI for the object.
@@ -1750,6 +1774,16 @@ def kms_key_name(self):
17501774
"DURABLE_REDUCED_AVAILABILITY", else ``None``.
17511775
"""
17521776

1777+
temporary_hold = _scalar_property('temporaryHold')
1778+
"""Is a temporary hold active on the object?
1779+
1780+
See `API reference docs`_.
1781+
1782+
If the property is not set locally, returns :data:`None`.
1783+
1784+
:rtype: bool or ``NoneType``
1785+
"""
1786+
17531787
@property
17541788
def time_deleted(self):
17551789
"""Retrieve the timestamp at which the object was deleted.

storage/tests/unit/test_blob.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2669,6 +2669,35 @@ def test_etag(self):
26692669
blob = self._make_one(BLOB_NAME, bucket=bucket, properties=properties)
26702670
self.assertEqual(blob.etag, ETAG)
26712671

2672+
def test_event_based_hold_getter_missing(self):
2673+
BLOB_NAME = 'blob-name'
2674+
bucket = _Bucket()
2675+
properties = {}
2676+
blob = self._make_one(BLOB_NAME, bucket=bucket, properties=properties)
2677+
self.assertIsNone(blob.event_based_hold)
2678+
2679+
def test_event_based_hold_getter_false(self):
2680+
BLOB_NAME = 'blob-name'
2681+
bucket = _Bucket()
2682+
properties = {'eventBasedHold': False}
2683+
blob = self._make_one(BLOB_NAME, bucket=bucket, properties=properties)
2684+
self.assertFalse(blob.event_based_hold)
2685+
2686+
def test_event_based_hold_getter_true(self):
2687+
BLOB_NAME = 'blob-name'
2688+
bucket = _Bucket()
2689+
properties = {'eventBasedHold': True}
2690+
blob = self._make_one(BLOB_NAME, bucket=bucket, properties=properties)
2691+
self.assertTrue(blob.event_based_hold)
2692+
2693+
def test_event_based_hold_setter(self):
2694+
BLOB_NAME = 'blob-name'
2695+
bucket = _Bucket()
2696+
blob = self._make_one(BLOB_NAME, bucket=bucket)
2697+
self.assertIsNone(blob.event_based_hold)
2698+
blob.event_based_hold = True
2699+
self.assertEqual(blob.event_based_hold, True)
2700+
26722701
def test_generation(self):
26732702
BUCKET = object()
26742703
GENERATION = 42
@@ -2768,6 +2797,23 @@ def test_owner(self):
27682797
self.assertEqual(owner['entity'], 'project-owner-12345')
27692798
self.assertEqual(owner['entityId'], '23456')
27702799

2800+
def test_retention_expiration_time(self):
2801+
from google.cloud._helpers import _RFC3339_MICROS
2802+
from google.cloud._helpers import UTC
2803+
2804+
BLOB_NAME = 'blob-name'
2805+
bucket = _Bucket()
2806+
TIMESTAMP = datetime.datetime(2014, 11, 5, 20, 34, 37, tzinfo=UTC)
2807+
TIME_CREATED = TIMESTAMP.strftime(_RFC3339_MICROS)
2808+
properties = {'retentionExpirationTime': TIME_CREATED}
2809+
blob = self._make_one(BLOB_NAME, bucket=bucket, properties=properties)
2810+
self.assertEqual(blob.retention_expiration_time, TIMESTAMP)
2811+
2812+
def test_retention_expiration_time_unset(self):
2813+
BUCKET = object()
2814+
blob = self._make_one('blob-name', bucket=BUCKET)
2815+
self.assertIsNone(blob.retention_expiration_time)
2816+
27712817
def test_self_link(self):
27722818
BLOB_NAME = 'blob-name'
27732819
bucket = _Bucket()
@@ -2813,6 +2859,35 @@ def test_storage_class_setter(self):
28132859
self.assertEqual(blob.storage_class, storage_class)
28142860
self.assertEqual(blob._properties, {'storageClass': storage_class})
28152861

2862+
def test_temporary_hold_getter_missing(self):
2863+
BLOB_NAME = 'blob-name'
2864+
bucket = _Bucket()
2865+
properties = {}
2866+
blob = self._make_one(BLOB_NAME, bucket=bucket, properties=properties)
2867+
self.assertIsNone(blob.temporary_hold)
2868+
2869+
def test_temporary_hold_getter_false(self):
2870+
BLOB_NAME = 'blob-name'
2871+
bucket = _Bucket()
2872+
properties = {'temporaryHold': False}
2873+
blob = self._make_one(BLOB_NAME, bucket=bucket, properties=properties)
2874+
self.assertFalse(blob.temporary_hold)
2875+
2876+
def test_temporary_hold_getter_true(self):
2877+
BLOB_NAME = 'blob-name'
2878+
bucket = _Bucket()
2879+
properties = {'temporaryHold': True}
2880+
blob = self._make_one(BLOB_NAME, bucket=bucket, properties=properties)
2881+
self.assertTrue(blob.temporary_hold)
2882+
2883+
def test_temporary_hold_setter(self):
2884+
BLOB_NAME = 'blob-name'
2885+
bucket = _Bucket()
2886+
blob = self._make_one(BLOB_NAME, bucket=bucket)
2887+
self.assertIsNone(blob.temporary_hold)
2888+
blob.temporary_hold = True
2889+
self.assertEqual(blob.temporary_hold, True)
2890+
28162891
def test_time_deleted(self):
28172892
from google.cloud._helpers import _RFC3339_MICROS
28182893
from google.cloud._helpers import UTC

0 commit comments

Comments
 (0)