Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 173
feat: add support for 'Blob.custom_time' and lifecycle rules#199
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
c03d6ae7347cf2ef32ca20212ca4a6c830c3dbda6a04e3a1075e60677ee2796e2793ce43f1f5a0bd9bf4fee993f76457b1ae71f29d99fcb962781a30fcf16039a12f0285e15b780a616e76b8bbFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -52,6 +52,7 @@ | ||
| from google.api_core.iam import Policy | ||
| from google.cloud import exceptions | ||
| from google.cloud._helpers import _bytes_to_unicode | ||
| from google.cloud._helpers import _datetime_to_rfc3339 | ||
| from google.cloud._helpers import _rfc3339_to_datetime | ||
| from google.cloud._helpers import _to_bytes | ||
| from google.cloud.exceptions import NotFound | ||
| @@ -3348,6 +3349,39 @@ def updated(self): | ||
| if value is not None: | ||
| return _rfc3339_to_datetime(value) | ||
| @property | ||
| def custom_time(self): | ||
| """Retrieve the custom time for the object. | ||
| See https://cloud.google.com/storage/docs/json_api/v1/objects | ||
| :rtype: :class:`datetime.datetime` or ``NoneType`` | ||
| :returns: Datetime object parsed from RFC3339 valid timestamp, or | ||
| ``None`` if the blob's resource has not been loaded from | ||
| the server (see :meth:`reload`). | ||
| """ | ||
| value = self._properties.get("customTime") | ||
| if value is not None: | ||
| return _rfc3339_to_datetime(value) | ||
| @custom_time.setter | ||
| def custom_time(self, value): | ||
| """Set the custom time for the object. Once set it can't be unset | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please follow PEP 257 for Multi-line docstrings, i.e., move the non-summary down below. | ||
| and only changed to a custom datetime in the future. If the | ||
| custom_time must be unset, you must either perform a rewrite operation | ||
| or upload the data again. | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These semantics are not enforced by the method. | ||
| See https://cloud.google.com/storage/docs/json_api/v1/objects | ||
| :type value: :class:`datetime.datetime` | ||
| :param value: (Optional) Set the custom time of blob. Datetime object | ||
| parsed from RFC3339 valid timestamp. | ||
| """ | ||
| if value is not None: | ||
| value = _datetime_to_rfc3339(value) | ||
| self._properties["customTime"] = value | ||
| def _get_encryption_headers(key, source=False): | ||
| """Builds customer encryption key headers | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -170,6 +170,18 @@ class LifecycleRuleConditions(dict): | ||
| :param number_of_newer_versions: (Optional) Apply rule action to versioned | ||
| items having N newer versions. | ||
| :type days_since_custom_time: int | ||
| :param days_since_custom_time: (Optional) Apply rule action to items whose number of days | ||
| elapsed since the custom 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 custom. | ||
| :type custom_time_before: :class:`datetime.date` | ||
| :param custom_time_before: (Optional) Date object parsed from RFC3339 valid date, apply rule action | ||
| to items whose custom time is before this date. This condition is relevant | ||
| only for versioned objects, e.g., 2019-03-16. | ||
| :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 | ||
| @@ -193,6 +205,8 @@ def __init__( | ||
| is_live=None, | ||
| matches_storage_class=None, | ||
| number_of_newer_versions=None, | ||
| days_since_custom_time=None, | ||
| custom_time_before=None, | ||
| days_since_noncurrent_time=None, | ||
| noncurrent_time_before=None, | ||
| _factory=False, | ||
| @@ -214,6 +228,12 @@ def __init__( | ||
| if number_of_newer_versions is not None: | ||
| conditions["numNewerVersions"] = number_of_newer_versions | ||
| if days_since_custom_time is not None: | ||
| conditions["daysSinceCustomTime"] = days_since_custom_time | ||
| if custom_time_before is not None: | ||
| conditions["customTimeBefore"] = custom_time_before.isoformat() | ||
| if not _factory and not conditions: | ||
| raise ValueError("Supply at least one condition") | ||
| @@ -266,6 +286,18 @@ def number_of_newer_versions(self): | ||
| """Conditon's 'number_of_newer_versions' value.""" | ||
| return self.get("numNewerVersions") | ||
| @property | ||
| def days_since_custom_time(self): | ||
| """Conditon's 'days_since_custom_time' value.""" | ||
| return self.get("daysSinceCustomTime") | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setter for this property? | ||
| @property | ||
| def custom_time_before(self): | ||
| """Conditon's 'custom_time_before' value.""" | ||
| before = self.get("customTimeBefore") | ||
| if before is not None: | ||
| return datetime_helpers.from_iso8601_date(before) | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setter for this property? ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure about the setter method for all this properties, because we don't have a method for other properties as well which defined before. Please let me know if needed, so need to create setter method for all the properties of Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks both, looks like setters weren't defined at this level. It will be filed as a feature request for now. It doesn't block customers from using the feature. | ||
| @property | ||
| def days_since_noncurrent_time(self): | ||
| """Conditon's 'days_since_noncurrent_time' value.""" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -157,6 +157,7 @@ def _set_properties_helper(self, kms_key_name=None): | ||
| "crc32c": CRC32C, | ||
| "componentCount": COMPONENT_COUNT, | ||
| "etag": ETAG, | ||
| "customTime": NOW, | ||
| } | ||
| if kms_key_name is not None: | ||
| @@ -188,6 +189,7 @@ def _set_properties_helper(self, kms_key_name=None): | ||
| self.assertEqual(blob.crc32c, CRC32C) | ||
| self.assertEqual(blob.component_count, COMPONENT_COUNT) | ||
| self.assertEqual(blob.etag, ETAG) | ||
| self.assertEqual(blob.custom_time, now) | ||
| if kms_key_name is not None: | ||
| self.assertEqual(blob.kms_key_name, kms_key_name) | ||
| @@ -4248,6 +4250,48 @@ def test_updated_unset(self): | ||
| blob = self._make_one("blob-name", bucket=BUCKET) | ||
| self.assertIsNone(blob.updated) | ||
| def test_custom_time_getter(self): | ||
| from google.cloud._helpers import _RFC3339_MICROS | ||
| from google.cloud._helpers import UTC | ||
| BLOB_NAME = "blob-name" | ||
| bucket = _Bucket() | ||
| TIMESTAMP = datetime.datetime(2014, 11, 5, 20, 34, 37, tzinfo=UTC) | ||
| TIME_CREATED = TIMESTAMP.strftime(_RFC3339_MICROS) | ||
| properties = {"customTime": TIME_CREATED} | ||
| blob = self._make_one(BLOB_NAME, bucket=bucket, properties=properties) | ||
| self.assertEqual(blob.custom_time, TIMESTAMP) | ||
| def test_custom_time_setter(self): | ||
| from google.cloud._helpers import UTC | ||
| BLOB_NAME = "blob-name" | ||
| bucket = _Bucket() | ||
| TIMESTAMP = datetime.datetime(2014, 11, 5, 20, 34, 37, tzinfo=UTC) | ||
| blob = self._make_one(BLOB_NAME, bucket=bucket) | ||
| self.assertIsNone(blob.custom_time) | ||
| blob.custom_time = TIMESTAMP | ||
| self.assertEqual(blob.custom_time, TIMESTAMP) | ||
tseaver marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| def test_custom_time_setter_none_value(self): | ||
| from google.cloud._helpers import _RFC3339_MICROS | ||
| from google.cloud._helpers import UTC | ||
| BLOB_NAME = "blob-name" | ||
| bucket = _Bucket() | ||
| TIMESTAMP = datetime.datetime(2014, 11, 5, 20, 34, 37, tzinfo=UTC) | ||
| TIME_CREATED = TIMESTAMP.strftime(_RFC3339_MICROS) | ||
| properties = {"customTime": TIME_CREATED} | ||
| blob = self._make_one(BLOB_NAME, bucket=bucket, properties=properties) | ||
| self.assertEqual(blob.custom_time, TIMESTAMP) | ||
| blob.custom_time = None | ||
| self.assertIsNone(blob.custom_time) | ||
tseaver marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| def test_custom_time_unset(self): | ||
| BUCKET = object() | ||
| blob = self._make_one("blob-name", bucket=BUCKET) | ||
| self.assertIsNone(blob.custom_time) | ||
| def test_from_string_w_valid_uri(self): | ||
| from google.cloud.storage.blob import Blob | ||
Uh oh!
There was an error while loading. Please reload this page.