Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(storage): add support of daysSinceNoncurrentTime and noncurrentTimeBefore #162

Merged
merged 21 commits into from Aug 25, 2020
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8677bdf
feat(storage): add support of daysSinceNoncurrentTime and noncurrentT…
HemangChothani May 27, 2020
463c75d
Merge branch 'master' of https://github.com/googleapis/python-storage…
HemangChothani May 27, 2020
d6351ab
feat(storage): fix code coverage
HemangChothani May 28, 2020
8b5b67f
Merge branch 'master' of https://github.com/googleapis/python-storage…
HemangChothani May 28, 2020
b866ffc
feat(storage): add custom method to convert datetime to string
HemangChothani May 28, 2020
39ff644
Merge branch 'master' of https://github.com/googleapis/python-storage…
HemangChothani Jun 12, 2020
be50284
Merge branch 'master' of https://github.com/googleapis/python-storage…
HemangChothani Jun 17, 2020
73030c6
feat(storage): remove custom method as server support microsec
HemangChothani Jun 23, 2020
1e8c3e0
Merge branch 'master' of https://github.com/googleapis/python-storage…
HemangChothani Jun 23, 2020
26c3990
Merge branch 'master' of https://github.com/googleapis/python-storage…
HemangChothani Jun 24, 2020
63f90dd
Merge branch 'master' into storage_issue_159
jkwlui Jul 7, 2020
f1b3fed
feat(storage): change the return type of noncurrent_time_before
HemangChothani Jul 17, 2020
a51ebf0
Merge branch 'storage_issue_159' of https://github.com/q-logic/python…
HemangChothani Jul 17, 2020
c314fa2
feat(storage): change non_current_time type from datetime to date
HemangChothani Aug 7, 2020
6ca1e26
Merge branch 'master' of https://github.com/googleapis/python-storage…
HemangChothani Aug 7, 2020
0f25ddf
Merge branch 'master' of https://github.com/googleapis/python-storage…
HemangChothani Aug 13, 2020
465af75
Merge branch 'master' of https://github.com/googleapis/python-storage…
HemangChothani Aug 17, 2020
5494c57
Merge branch 'master' into storage_issue_159
frankyn Aug 24, 2020
4b51e38
feat: nit
HemangChothani Aug 25, 2020
bd2edda
Merge branch 'storage_issue_159' of https://github.com/q-logic/python…
HemangChothani Aug 25, 2020
c805a01
Merge branch 'master' of https://github.com/googleapis/python-storage…
HemangChothani Aug 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 37 additions & 2 deletions google/cloud/storage/bucket.py
Expand Up @@ -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
Expand All @@ -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.datetime`
:param noncurrent_time_before: (Optional) Datetime object parsed from RFC3339 valid timestamp, apply
rule action to items whose non current time is before this timestamp.
This condition is relevant only for versioned objects.

:raises ValueError: if no arguments are passed.
"""

Expand All @@ -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 = {}
Expand All @@ -202,6 +217,14 @@ 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"] = _datetime_to_rfc3339(
noncurrent_time_before
)

super(LifecycleRuleConditions, self).__init__(conditions)

@classmethod
Expand Down Expand Up @@ -245,6 +268,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."""
timestamp = self.get("noncurrentTimeBefore")
if timestamp is not None:
return _rfc3339_to_datetime(timestamp)


class LifecycleRuleDelete(dict):
"""Map a lifecycle rule deleting matching items.
Expand Down Expand Up @@ -274,7 +309,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.
Expand Down
17 changes: 15 additions & 2 deletions tests/system/test_system.py
Expand Up @@ -191,22 +191,35 @@ 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.datetime.now() + datetime.timedelta(days=10)
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,
matches_storage_class=[constants.NEARLINE_STORAGE_CLASS],
)

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,
Expand Down
48 changes: 48 additions & 0 deletions tests/unit/test_bucket.py
Expand Up @@ -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)
Expand All @@ -88,24 +89,71 @@ 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
import pytz
from google.cloud._helpers import _datetime_to_rfc3339

noncurrent_before = datetime.datetime.utcnow().replace(
tzinfo=pytz.UTC
) + datetime.timedelta(days=10)
conditions = self._make_one(
number_of_newer_versions=3, noncurrent_time_before=noncurrent_before
)

expected = {
"numNewerVersions": 3,
"noncurrentTimeBefore": _datetime_to_rfc3339(noncurrent_before),
}
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
import pytz
from google.cloud._helpers import _datetime_to_rfc3339

noncurrent_before = datetime.datetime.utcnow().replace(
tzinfo=pytz.UTC
) + datetime.timedelta(days=10)
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": _datetime_to_rfc3339(noncurrent_before),
}
conditions = klass.from_api_repr(resource)
self.assertEqual(conditions.age, 10)
self.assertEqual(conditions.created_before, before)
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):
Expand Down