diff --git a/google/cloud/storage/bucket.py b/google/cloud/storage/bucket.py index ad7eaf6df..a0ef863bb 100644 --- a/google/cloud/storage/bucket.py +++ b/google/cloud/storage/bucket.py @@ -148,7 +148,7 @@ class LifecycleRuleConditions(dict): See: https://cloud.google.com/storage/docs/lifecycle :type age: int - :param age: (Optional) Apply rule action to items whos age, in days, + :param age: (Optional) Apply rule action to items whose age, in days, exceeds this value. :type created_before: datetime.date @@ -170,6 +170,19 @@ class LifecycleRuleConditions(dict): :param number_of_newer_versions: (Optional) Apply rule action to versioned items having N newer versions. + :type days_since_noncurrent_time: int + :param days_since_noncurrent_time: (Optional) Apply rule action to items whose number of days + elapsed since the non current timestamp. This condition + is relevant only for versioned objects. The value of the field + must be a non negative integer. If it's zero, the object version + will become eligible for lifecycle action as soon as it becomes + non current. + + :type noncurrent_time_before: :class:`datetime.date` + :param noncurrent_time_before: (Optional) Date object parsed from RFC3339 valid date, apply + rule action to items whose non current time is before this date. + This condition is relevant only for versioned objects, e.g, 2019-03-16. + :raises ValueError: if no arguments are passed. """ @@ -180,6 +193,8 @@ def __init__( is_live=None, matches_storage_class=None, number_of_newer_versions=None, + days_since_noncurrent_time=None, + noncurrent_time_before=None, _factory=False, ): conditions = {} @@ -202,6 +217,12 @@ def __init__( if not _factory and not conditions: raise ValueError("Supply at least one condition") + if days_since_noncurrent_time is not None: + conditions["daysSinceNoncurrentTime"] = days_since_noncurrent_time + + if noncurrent_time_before is not None: + conditions["noncurrentTimeBefore"] = noncurrent_time_before.isoformat() + super(LifecycleRuleConditions, self).__init__(conditions) @classmethod @@ -245,6 +266,18 @@ def number_of_newer_versions(self): """Conditon's 'number_of_newer_versions' value.""" return self.get("numNewerVersions") + @property + def days_since_noncurrent_time(self): + """Conditon's 'days_since_noncurrent_time' value.""" + return self.get("daysSinceNoncurrentTime") + + @property + def noncurrent_time_before(self): + """Conditon's 'noncurrent_time_before' value.""" + before = self.get("noncurrentTimeBefore") + if before is not None: + return datetime_helpers.from_iso8601_date(before) + class LifecycleRuleDelete(dict): """Map a lifecycle rule deleting matching items. @@ -274,7 +307,7 @@ def from_api_repr(cls, resource): class LifecycleRuleSetStorageClass(dict): - """Map a lifecycle rule upating storage class of matching items. + """Map a lifecycle rule updating storage class of matching items. :type storage_class: str, one of :attr:`Bucket.STORAGE_CLASSES`. :param storage_class: new storage class to assign to matching items. diff --git a/tests/system/test_system.py b/tests/system/test_system.py index 25b347f83..e5ddc648a 100644 --- a/tests/system/test_system.py +++ b/tests/system/test_system.py @@ -192,14 +192,22 @@ def test_bucket_create_w_alt_storage_class(self): self.assertEqual(created.storage_class, constants.ARCHIVE_STORAGE_CLASS) def test_lifecycle_rules(self): + import datetime from google.cloud.storage import constants new_bucket_name = "w-lifcycle-rules" + unique_resource_id("-") + noncurrent_before = datetime.date(2018, 8, 1) self.assertRaises( exceptions.NotFound, Config.CLIENT.get_bucket, new_bucket_name ) bucket = Config.CLIENT.bucket(new_bucket_name) - bucket.add_lifecycle_delete_rule(age=42) + bucket.add_lifecycle_delete_rule( + age=42, + number_of_newer_versions=3, + days_since_noncurrent_time=2, + noncurrent_time_before=noncurrent_before, + ) + bucket.add_lifecycle_set_storage_class_rule( constants.COLDLINE_STORAGE_CLASS, is_live=False, @@ -207,7 +215,12 @@ def test_lifecycle_rules(self): ) expected_rules = [ - LifecycleRuleDelete(age=42), + LifecycleRuleDelete( + age=42, + number_of_newer_versions=3, + days_since_noncurrent_time=2, + noncurrent_time_before=noncurrent_before, + ), LifecycleRuleSetStorageClass( constants.COLDLINE_STORAGE_CLASS, is_live=False, diff --git a/tests/unit/test_bucket.py b/tests/unit/test_bucket.py index 3c5f2e68d..2336416c4 100644 --- a/tests/unit/test_bucket.py +++ b/tests/unit/test_bucket.py @@ -77,6 +77,7 @@ def test_ctor_w_created_before_and_is_live(self): self.assertEqual(conditions.is_live, False) self.assertIsNone(conditions.matches_storage_class) self.assertIsNone(conditions.number_of_newer_versions) + self.assertIsNone(conditions.noncurrent_time_before) def test_ctor_w_number_of_newer_versions(self): conditions = self._make_one(number_of_newer_versions=3) @@ -88,17 +89,54 @@ def test_ctor_w_number_of_newer_versions(self): self.assertIsNone(conditions.matches_storage_class) self.assertEqual(conditions.number_of_newer_versions, 3) + def test_ctor_w_days_since_noncurrent_time(self): + conditions = self._make_one( + number_of_newer_versions=3, days_since_noncurrent_time=2 + ) + expected = {"numNewerVersions": 3, "daysSinceNoncurrentTime": 2} + self.assertEqual(dict(conditions), expected) + self.assertIsNone(conditions.age) + self.assertIsNone(conditions.created_before) + self.assertIsNone(conditions.is_live) + self.assertIsNone(conditions.matches_storage_class) + self.assertEqual(conditions.number_of_newer_versions, 3) + self.assertEqual(conditions.days_since_noncurrent_time, 2) + + def test_ctor_w_noncurrent_time_before(self): + import datetime + + noncurrent_before = datetime.date(2018, 8, 1) + conditions = self._make_one( + number_of_newer_versions=3, noncurrent_time_before=noncurrent_before + ) + + expected = { + "numNewerVersions": 3, + "noncurrentTimeBefore": noncurrent_before.isoformat(), + } + self.assertEqual(dict(conditions), expected) + self.assertIsNone(conditions.age) + self.assertIsNone(conditions.created_before) + self.assertIsNone(conditions.is_live) + self.assertIsNone(conditions.matches_storage_class) + self.assertEqual(conditions.number_of_newer_versions, 3) + self.assertEqual(conditions.noncurrent_time_before, noncurrent_before) + def test_from_api_repr(self): import datetime + noncurrent_before = datetime.date(2018, 8, 1) before = datetime.date(2018, 8, 1) klass = self._get_target_class() + resource = { "age": 10, "createdBefore": "2018-08-01", "isLive": True, "matchesStorageClass": ["COLDLINE"], "numNewerVersions": 3, + "daysSinceNoncurrentTime": 2, + "noncurrentTimeBefore": noncurrent_before.isoformat(), } conditions = klass.from_api_repr(resource) self.assertEqual(conditions.age, 10) @@ -106,6 +144,8 @@ def test_from_api_repr(self): self.assertEqual(conditions.is_live, True) self.assertEqual(conditions.matches_storage_class, ["COLDLINE"]) self.assertEqual(conditions.number_of_newer_versions, 3) + self.assertEqual(conditions.days_since_noncurrent_time, 2) + self.assertEqual(conditions.noncurrent_time_before, noncurrent_before) class Test_LifecycleRuleDelete(unittest.TestCase):