Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Allow to overwrite date to check expiry against using the `SOURCE_DATE_EPOCH` environment variable ([bug 2052175](https://bugzilla.mozilla.org/show_bug.cgi?id=2052175))

## 20.0.1

- Ensure `in_session` is present for all metrics in the object model, like the other CommonMetricData args ([bug 2046193](https://bugzilla.mozilla.org/show_bug.cgi?id=2046193)).
Expand Down
13 changes: 12 additions & 1 deletion glean_parser/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import datetime
import time
import os
import functools
import json
from pathlib import Path
Expand Down Expand Up @@ -412,6 +414,15 @@ def parse_expiration_version(expires: str) -> int:
)


def now_epoch():
try:
epoch_ts = int(os.environ.get("SOURCE_DATE_EPOCH", time.time()))
except ValueError:
raise ValueError("Invalid SOURCE_DATE_EPOCH")

return datetime.datetime.fromtimestamp(epoch_ts, tz=datetime.timezone.utc)


def is_expired(expires: str, major_version: Optional[int] = None) -> bool:
"""
Parses the `expires` field in a metric or ping and returns whether
Expand All @@ -425,7 +436,7 @@ def is_expired(expires: str, major_version: Optional[int] = None) -> bool:
return parse_expiration_version(expires) <= major_version
else:
date = parse_expiration_date(expires)
return date <= datetime.datetime.now(datetime.timezone.utc).date()
return date <= now_epoch().date()


def validate_expires(expires: str, major_version: Optional[int] = None) -> None:
Expand Down
61 changes: 61 additions & 0 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import json
import re
import textwrap
import os
import datetime

import pytest

Expand Down Expand Up @@ -1450,3 +1452,62 @@ def test_forbid_non_ext_non_object_attribution_distribution():
"Extended attribution/distribution metrics must be of type 'object'"
in errors[1]
)


def test_expire_epoch_can_be_overriden():
metrics = [
{
"category": {
"metric": {
"type": "boolean",
"expires": "2023-12-31", # definitely in the past
},
}
}
]
metrics = [util.add_required(x) for x in metrics]

all_metrics = parser.parse_objects(metrics)
errors = list(all_metrics)
assert len(errors) == 0
assert all_metrics.value["category"]["metric"].disabled is True

# A date definitely before the above expiry.
dt = datetime.datetime.strptime("2020-01-01", "%Y-%m-%d")
os.environ["SOURCE_DATE_EPOCH"] = str(int(dt.timestamp()))

all_metrics = parser.parse_objects(metrics)
errors = list(all_metrics)
assert len(errors) == 0
assert all_metrics.value["category"]["metric"].disabled is False

del os.environ["SOURCE_DATE_EPOCH"]


@pytest.mark.parametrize(
"invalid_epoch",
[
"not-a-date",
"2020-01-01",
"",
],
)
def test_overriden_expire_epoch_must_be_valid(invalid_epoch):
metrics = [
{
"category": {
"metric": {
"type": "boolean",
"expires": "2023-12-31", # definitely in the past
},
}
}
]
metrics = [util.add_required(x) for x in metrics]

with pytest.raises(ValueError, match="Invalid SOURCE_DATE_EPOCH"):
os.environ["SOURCE_DATE_EPOCH"] = invalid_epoch
all_metrics = parser.parse_objects(metrics)
list(all_metrics)

del os.environ["SOURCE_DATE_EPOCH"]