From b7a156652faf0e014391126b979824cab81a2f92 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Mon, 5 Mar 2018 13:09:10 -0500 Subject: [PATCH] Add bucket properties to support retention policy feature. (#447) Toward #445. --- storage/google/cloud/storage/bucket.py | 73 ++++++++++++++++++++++ storage/tests/unit/test_bucket.py | 86 ++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) diff --git a/storage/google/cloud/storage/bucket.py b/storage/google/cloud/storage/bucket.py index 0d65f92a8ab5..f34acecb6054 100644 --- a/storage/google/cloud/storage/bucket.py +++ b/storage/google/cloud/storage/bucket.py @@ -972,6 +972,21 @@ def cors(self, entries): """ self._patch_property('cors', entries) + default_event_based_hold = _scalar_property('defaultEventBasedHold') + """Are uploaded objects automatically placed under an even-based hold? + + If True, uploaded objects will be placed under an event-based hold to + be released at a future time. When released an object will then begin + the retention period determined by the policy retention period for the + object bucket. + + See https://cloud.google.com/storage/docs/json_api/v1/buckets + + If the property is not set locally, returns ``None``. + + :rtype: bool or ``NoneType`` + """ + @property def default_kms_key_name(self): """Retrieve / set default KMS encryption key for objects in the bucket. @@ -1276,6 +1291,64 @@ def project_number(self): if project_number is not None: return int(project_number) + @property + def retention_policy_effective_time(self): + """Retrieve the effective time of the bucket's retention policy. + + :rtype: datetime.datetime or ``NoneType`` + :returns: point-in time at which the bucket's retention policy is + effective, or ``None`` if the property is not + set locally. + """ + policy = self._properties.get('retentionPolicy') + if policy is not None: + timestamp = policy.get('effectiveTime') + if timestamp is not None: + return _rfc3339_to_datetime(timestamp) + + @property + def retention_policy_locked(self): + """Retrieve whthere the bucket's retention policy is locked. + + :rtype: bool + :returns: True if the bucket's policy is locked, or else False + if the policy is not locked, or the property is not + set locally. + """ + policy = self._properties.get('retentionPolicy') + if policy is not None: + return policy.get('isLocked') + + @property + def retention_period(self): + """Retrieve or set the retention period for items in the bucket. + + :rtype: int or ``NoneType`` + :returns: number of seconds to retain items after upload or release + from event-based lock, or ``None`` if the property is not + set locally. + """ + policy = self._properties.get('retentionPolicy') + if policy is not None: + period = policy.get('retentionPeriod') + if period is not None: + return int(period) + + @retention_period.setter + def retention_period(self, value): + """Set the retention period for items in the bucket. + + :type value: int + :param value: + number of seconds to retain items after upload or release from + event-based lock. + + :raises ValueError: if the bucket's retention policy is locked. + """ + policy = self._properties.setdefault('retentionPolicy', {}) + policy['retentionPeriod'] = str(value) + self._patch_property('retentionPolicy', policy) + @property def self_link(self): """Retrieve the URI for the bucket. diff --git a/storage/tests/unit/test_bucket.py b/storage/tests/unit/test_bucket.py index db3ebd961496..16d03d1afd2d 100644 --- a/storage/tests/unit/test_bucket.py +++ b/storage/tests/unit/test_bucket.py @@ -1494,6 +1494,92 @@ def test_project_number_string_val(self): bucket = self._make_one(properties=properties) self.assertEqual(bucket.project_number, PROJECT_NUMBER) + def test_retention_policy_effective_time_policy_missing(self): + bucket = self._make_one() + self.assertIsNone(bucket.retention_policy_effective_time) + + def test_retention_policy_effective_time_et_missing(self): + properties = { + 'retentionPolicy': { + }, + } + bucket = self._make_one(properties=properties) + + self.assertIsNone(bucket.retention_policy_effective_time) + + def test_retention_policy_effective_time(self): + import datetime + from google.cloud._helpers import _datetime_to_rfc3339 + from google.cloud._helpers import UTC + + effective_time = datetime.datetime.utcnow().replace(tzinfo=UTC) + properties = { + 'retentionPolicy': { + 'effectiveTime': _datetime_to_rfc3339(effective_time), + }, + } + bucket = self._make_one(properties=properties) + + self.assertEqual( + bucket.retention_policy_effective_time, effective_time) + + def test_retention_policy_locked_missing(self): + bucket = self._make_one() + self.assertFalse(bucket.retention_policy_locked) + + def test_retention_policy_locked_false(self): + properties = { + 'retentionPolicy': { + 'isLocked': False, + }, + } + bucket = self._make_one(properties=properties) + self.assertFalse(bucket.retention_policy_locked) + + def test_retention_policy_locked_true(self): + properties = { + 'retentionPolicy': { + 'isLocked': True, + }, + } + bucket = self._make_one(properties=properties) + self.assertTrue(bucket.retention_policy_locked) + + def test_retention_period_getter_policymissing(self): + bucket = self._make_one() + + self.assertIsNone(bucket.retention_period) + + def test_retention_period_getter_pr_missing(self): + properties = { + 'retentionPolicy': { + }, + } + bucket = self._make_one(properties=properties) + + self.assertIsNone(bucket.retention_period) + + def test_retention_period_getter(self): + period = 86400 * 100 # 100 days + properties = { + 'retentionPolicy': { + 'retentionPeriod': str(period), + }, + } + bucket = self._make_one(properties=properties) + + self.assertEqual(bucket.retention_period, period) + + def test_retention_period_setter(self): + period = 86400 * 100 # 100 days + bucket = self._make_one() + + bucket.retention_period = period + + self.assertEqual( + bucket._properties['retentionPolicy']['retentionPeriod'], + str(period)) + def test_self_link(self): SELF_LINK = 'http://example.com/self/' properties = {'selfLink': SELF_LINK}