Skip to content

Commit 136c097

Browse files
HemangChothanijkwluifrankyn
authored
feat(storage): add support of daysSinceNoncurrentTime and noncurrentTimeBefore (#162)
* feat(storage): add support of daysSinceNoncurrentTime and noncurrentTimeBefore * feat(storage): fix code coverage * feat(storage): add custom method to convert datetime to string * feat(storage): remove custom method as server support microsec * feat(storage): change the return type of noncurrent_time_before * feat(storage): change non_current_time type from datetime to date * feat: nit Co-authored-by: Jonathan Lui <[email protected]> Co-authored-by: Frank Natividad <[email protected]>
1 parent 413f7b5 commit 136c097

File tree

3 files changed

+90
-4
lines changed

3 files changed

+90
-4
lines changed

google/cloud/storage/bucket.py

+35-2
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ class LifecycleRuleConditions(dict):
148148
See: https://cloud.google.com/storage/docs/lifecycle
149149
150150
:type age: int
151-
:param age: (Optional) Apply rule action to items whos age, in days,
151+
:param age: (Optional) Apply rule action to items whose age, in days,
152152
exceeds this value.
153153
154154
:type created_before: datetime.date
@@ -170,6 +170,19 @@ class LifecycleRuleConditions(dict):
170170
:param number_of_newer_versions: (Optional) Apply rule action to versioned
171171
items having N newer versions.
172172
173+
:type days_since_noncurrent_time: int
174+
:param days_since_noncurrent_time: (Optional) Apply rule action to items whose number of days
175+
elapsed since the non current timestamp. This condition
176+
is relevant only for versioned objects. The value of the field
177+
must be a non negative integer. If it's zero, the object version
178+
will become eligible for lifecycle action as soon as it becomes
179+
non current.
180+
181+
:type noncurrent_time_before: :class:`datetime.date`
182+
:param noncurrent_time_before: (Optional) Date object parsed from RFC3339 valid date, apply
183+
rule action to items whose non current time is before this date.
184+
This condition is relevant only for versioned objects, e.g, 2019-03-16.
185+
173186
:raises ValueError: if no arguments are passed.
174187
"""
175188

@@ -180,6 +193,8 @@ def __init__(
180193
is_live=None,
181194
matches_storage_class=None,
182195
number_of_newer_versions=None,
196+
days_since_noncurrent_time=None,
197+
noncurrent_time_before=None,
183198
_factory=False,
184199
):
185200
conditions = {}
@@ -202,6 +217,12 @@ def __init__(
202217
if not _factory and not conditions:
203218
raise ValueError("Supply at least one condition")
204219

220+
if days_since_noncurrent_time is not None:
221+
conditions["daysSinceNoncurrentTime"] = days_since_noncurrent_time
222+
223+
if noncurrent_time_before is not None:
224+
conditions["noncurrentTimeBefore"] = noncurrent_time_before.isoformat()
225+
205226
super(LifecycleRuleConditions, self).__init__(conditions)
206227

207228
@classmethod
@@ -245,6 +266,18 @@ def number_of_newer_versions(self):
245266
"""Conditon's 'number_of_newer_versions' value."""
246267
return self.get("numNewerVersions")
247268

269+
@property
270+
def days_since_noncurrent_time(self):
271+
"""Conditon's 'days_since_noncurrent_time' value."""
272+
return self.get("daysSinceNoncurrentTime")
273+
274+
@property
275+
def noncurrent_time_before(self):
276+
"""Conditon's 'noncurrent_time_before' value."""
277+
before = self.get("noncurrentTimeBefore")
278+
if before is not None:
279+
return datetime_helpers.from_iso8601_date(before)
280+
248281

249282
class LifecycleRuleDelete(dict):
250283
"""Map a lifecycle rule deleting matching items.
@@ -274,7 +307,7 @@ def from_api_repr(cls, resource):
274307

275308

276309
class LifecycleRuleSetStorageClass(dict):
277-
"""Map a lifecycle rule upating storage class of matching items.
310+
"""Map a lifecycle rule updating storage class of matching items.
278311
279312
:type storage_class: str, one of :attr:`Bucket.STORAGE_CLASSES`.
280313
:param storage_class: new storage class to assign to matching items.

tests/system/test_system.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -192,22 +192,35 @@ def test_bucket_create_w_alt_storage_class(self):
192192
self.assertEqual(created.storage_class, constants.ARCHIVE_STORAGE_CLASS)
193193

194194
def test_lifecycle_rules(self):
195+
import datetime
195196
from google.cloud.storage import constants
196197

197198
new_bucket_name = "w-lifcycle-rules" + unique_resource_id("-")
199+
noncurrent_before = datetime.date(2018, 8, 1)
198200
self.assertRaises(
199201
exceptions.NotFound, Config.CLIENT.get_bucket, new_bucket_name
200202
)
201203
bucket = Config.CLIENT.bucket(new_bucket_name)
202-
bucket.add_lifecycle_delete_rule(age=42)
204+
bucket.add_lifecycle_delete_rule(
205+
age=42,
206+
number_of_newer_versions=3,
207+
days_since_noncurrent_time=2,
208+
noncurrent_time_before=noncurrent_before,
209+
)
210+
203211
bucket.add_lifecycle_set_storage_class_rule(
204212
constants.COLDLINE_STORAGE_CLASS,
205213
is_live=False,
206214
matches_storage_class=[constants.NEARLINE_STORAGE_CLASS],
207215
)
208216

209217
expected_rules = [
210-
LifecycleRuleDelete(age=42),
218+
LifecycleRuleDelete(
219+
age=42,
220+
number_of_newer_versions=3,
221+
days_since_noncurrent_time=2,
222+
noncurrent_time_before=noncurrent_before,
223+
),
211224
LifecycleRuleSetStorageClass(
212225
constants.COLDLINE_STORAGE_CLASS,
213226
is_live=False,

tests/unit/test_bucket.py

+40
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def test_ctor_w_created_before_and_is_live(self):
7777
self.assertEqual(conditions.is_live, False)
7878
self.assertIsNone(conditions.matches_storage_class)
7979
self.assertIsNone(conditions.number_of_newer_versions)
80+
self.assertIsNone(conditions.noncurrent_time_before)
8081

8182
def test_ctor_w_number_of_newer_versions(self):
8283
conditions = self._make_one(number_of_newer_versions=3)
@@ -88,24 +89,63 @@ def test_ctor_w_number_of_newer_versions(self):
8889
self.assertIsNone(conditions.matches_storage_class)
8990
self.assertEqual(conditions.number_of_newer_versions, 3)
9091

92+
def test_ctor_w_days_since_noncurrent_time(self):
93+
conditions = self._make_one(
94+
number_of_newer_versions=3, days_since_noncurrent_time=2
95+
)
96+
expected = {"numNewerVersions": 3, "daysSinceNoncurrentTime": 2}
97+
self.assertEqual(dict(conditions), expected)
98+
self.assertIsNone(conditions.age)
99+
self.assertIsNone(conditions.created_before)
100+
self.assertIsNone(conditions.is_live)
101+
self.assertIsNone(conditions.matches_storage_class)
102+
self.assertEqual(conditions.number_of_newer_versions, 3)
103+
self.assertEqual(conditions.days_since_noncurrent_time, 2)
104+
105+
def test_ctor_w_noncurrent_time_before(self):
106+
import datetime
107+
108+
noncurrent_before = datetime.date(2018, 8, 1)
109+
conditions = self._make_one(
110+
number_of_newer_versions=3, noncurrent_time_before=noncurrent_before
111+
)
112+
113+
expected = {
114+
"numNewerVersions": 3,
115+
"noncurrentTimeBefore": noncurrent_before.isoformat(),
116+
}
117+
self.assertEqual(dict(conditions), expected)
118+
self.assertIsNone(conditions.age)
119+
self.assertIsNone(conditions.created_before)
120+
self.assertIsNone(conditions.is_live)
121+
self.assertIsNone(conditions.matches_storage_class)
122+
self.assertEqual(conditions.number_of_newer_versions, 3)
123+
self.assertEqual(conditions.noncurrent_time_before, noncurrent_before)
124+
91125
def test_from_api_repr(self):
92126
import datetime
93127

128+
noncurrent_before = datetime.date(2018, 8, 1)
94129
before = datetime.date(2018, 8, 1)
95130
klass = self._get_target_class()
131+
96132
resource = {
97133
"age": 10,
98134
"createdBefore": "2018-08-01",
99135
"isLive": True,
100136
"matchesStorageClass": ["COLDLINE"],
101137
"numNewerVersions": 3,
138+
"daysSinceNoncurrentTime": 2,
139+
"noncurrentTimeBefore": noncurrent_before.isoformat(),
102140
}
103141
conditions = klass.from_api_repr(resource)
104142
self.assertEqual(conditions.age, 10)
105143
self.assertEqual(conditions.created_before, before)
106144
self.assertEqual(conditions.is_live, True)
107145
self.assertEqual(conditions.matches_storage_class, ["COLDLINE"])
108146
self.assertEqual(conditions.number_of_newer_versions, 3)
147+
self.assertEqual(conditions.days_since_noncurrent_time, 2)
148+
self.assertEqual(conditions.noncurrent_time_before, noncurrent_before)
109149

110150

111151
class Test_LifecycleRuleDelete(unittest.TestCase):

0 commit comments

Comments
 (0)