-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Validate --event-time-start
is before --event-time-end
#10820
Changes from 5 commits
13f25cc
5dc16b9
ee994c1
ca9b381
7b995c5
cad0006
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
kind: Features | ||
body: Ensure `--event-time-start` is before `--event-time-end` | ||
time: 2024-10-03T17:05:29.984398-05:00 | ||
custom: | ||
Author: QMalcolm | ||
Issue: "10786" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
import os | ||
import sys | ||
from dataclasses import dataclass | ||
from datetime import datetime | ||
from importlib import import_module | ||
from pathlib import Path | ||
from pprint import pformat as pf | ||
|
@@ -313,6 +314,9 @@ | |
self._assert_mutually_exclusive(params_assigned_from_default, ["SELECT", "INLINE"]) | ||
self._assert_mutually_exclusive(params_assigned_from_default, ["SELECTOR", "INLINE"]) | ||
|
||
# Check event_time configs for validity | ||
self._validate_event_time_configs() | ||
|
||
# Support lower cased access for legacy code. | ||
params = set( | ||
x for x in dir(self) if not callable(getattr(self, x)) and not x.startswith("__") | ||
|
@@ -349,6 +353,24 @@ | |
elif flag_set_by_user: | ||
set_flag = flag | ||
|
||
def _validate_event_time_configs(self) -> None: | ||
event_time_start = ( | ||
getattr(self, "EVENT_TIME_START") if hasattr(self, "EVENT_TIME_START") else None | ||
) | ||
if event_time_start is not None: | ||
event_time_end = ( | ||
getattr(self, "EVENT_TIME_END") if hasattr(self, "EVENT_TIME_END") else None | ||
) | ||
|
||
if event_time_end is not None and event_time_start >= event_time_end: | ||
raise DbtUsageException( | ||
"Value for `--event-time-start` must be less than `--event-time-end`" | ||
) | ||
elif event_time_start >= datetime.now(): | ||
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 are two separate conditions, so let's do two 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. To the first part, they are not two separate As for the second part, maybe? The issue is that the click parsing of datetimes creates naive datetimes (lacking timezone information). It won't even parse something like
So our options are to either assume what they give us is UTC, and give the passed in |
||
raise DbtUsageException( | ||
"Value for `--event-time-start` must be less than the current time if `--event-time-end` is not specififed" | ||
) | ||
|
||
def fire_deprecations(self): | ||
"""Fires events for deprecated env_var usage.""" | ||
[dep_fn() for dep_fn in self.deprecated_env_var_warnings] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
from datetime import datetime | ||
|
||
import pytest | ||
from freezegun import freeze_time | ||
|
||
from dbt.tests.util import run_dbt | ||
|
||
|
||
class TestEventTimeEndEventTimeStart: | ||
@pytest.mark.parametrize( | ||
"event_time_start,event_time_end,expect_pass", | ||
[ | ||
("2024-10-01", "2024-10-02", True), | ||
("2024-10-02", "2024-10-01", False), | ||
], | ||
) | ||
def test_option_combo(self, project, event_time_start, event_time_end, expect_pass): | ||
try: | ||
run_dbt( | ||
[ | ||
"build", | ||
"--event-time-start", | ||
event_time_start, | ||
"--event-time-end", | ||
event_time_end, | ||
] | ||
) | ||
assert expect_pass | ||
except Exception as e: | ||
assert ( | ||
"Value for `--event-time-start` must be less than `--event-time-end`" | ||
in e.__str__() | ||
) | ||
assert not expect_pass | ||
|
||
|
||
class TestEventTimeStartCurrent_time: | ||
@pytest.mark.parametrize( | ||
"event_time_start,current_time,expect_pass", | ||
[ | ||
("2024-10-01", "2024-10-02", True), | ||
("2024-10-02", "2024-10-01", False), | ||
], | ||
) | ||
def test_option_combo(self, project, event_time_start, current_time, expect_pass): | ||
with freeze_time(datetime.fromisoformat(current_time)): | ||
try: | ||
run_dbt(["build", "--event-time-start", event_time_start]) | ||
assert expect_pass | ||
except Exception as e: | ||
assert ( | ||
"Value for `--event-time-start` must be less than the current time if `--event-time-end` is not specififed" | ||
in e.__str__() | ||
) | ||
assert not expect_pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: do we need this level of nesting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so? We need to check whether or not
event_time_start
isNone
, as it only makes sense to do the validation ifevent_time_start
is a datetime value. We could get around this layer of nesting by doing something like:I'm personally not a huge fan of multiple returns. I can see how doing an early return would reduce our level of if's from 2 down to 1. However, given we're only at 2 levels of if's that seems unnecessary.
Side note that's not applicable here, but a corollary to my above "I avoid multiple returns".
If there were more levels of nesting, I still likely woudn't add early returns. I'd instead end up looking at splitting it into multiple functions, thus continuing allowing to maintain low levels of if context nesting and avoiding early multiple returns in a function.