From 57c58f5bc8cb08bc6e7a90167f47ab4f0d86ca51 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 22:49:46 +0000 Subject: [PATCH] fix: improve datetime parsing and validation in OAuth authenticator Co-Authored-By: Aaron Steers --- airbyte_cdk/sources/declarative/auth/oauth.py | 26 ++++++++++++------- .../requests_native_auth/abstract_oauth.py | 9 +++++-- airbyte_cdk/utils/datetime_helpers.py | 10 +++---- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/airbyte_cdk/sources/declarative/auth/oauth.py b/airbyte_cdk/sources/declarative/auth/oauth.py index 3133a544..22e5a0d7 100644 --- a/airbyte_cdk/sources/declarative/auth/oauth.py +++ b/airbyte_cdk/sources/declarative/auth/oauth.py @@ -122,18 +122,24 @@ def __post_init__(self, parameters: Mapping[str, Any]) -> None: self._refresh_request_headers = InterpolatedMapping( self.refresh_request_headers or {}, parameters=parameters ) - if isinstance(self.token_expiry_date, (int, str)) and str(self.token_expiry_date).isdigit(): - self._token_expiry_date = parse(self.token_expiry_date) - else: - self._token_expiry_date = ( - parse( - InterpolatedString.create(self.token_expiry_date, parameters=parameters).eval( - self.config + try: + if ( + isinstance(self.token_expiry_date, (int, str)) + and str(self.token_expiry_date).isdigit() + ): + self._token_expiry_date = parse(self.token_expiry_date) + else: + self._token_expiry_date = ( + parse( + InterpolatedString.create( + self.token_expiry_date, parameters=parameters + ).eval(self.config) ) + if self.token_expiry_date + else now() - timedelta(days=1) ) - if self.token_expiry_date - else now() - timedelta(days=1) - ) + except ValueError as e: + raise ValueError(f"Invalid token expiry date format: {e}") self.use_profile_assertion = ( InterpolatedBoolean(self.use_profile_assertion, parameters=parameters) if isinstance(self.use_profile_assertion, str) diff --git a/airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py b/airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py index 6c879c94..df3065c0 100644 --- a/airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py +++ b/airbyte_cdk/sources/streams/http/requests_native_auth/abstract_oauth.py @@ -191,14 +191,19 @@ def _parse_token_expiration_date(self, value: Union[str, int]) -> AirbyteDateTim raise ValueError( f"Invalid token expiry date format {self.token_expiry_date_format}; a string representing the format is required." ) - return parse(str(value)) + try: + return parse(str(value)) + except ValueError as e: + raise ValueError(f"Invalid token expiry date format: {e}") else: try: # Only accept numeric values (as int/float/string) when no format specified seconds = int(float(str(value))) return add_seconds(now(), seconds) except (ValueError, TypeError): - raise ValueError(f"Invalid expires_in value: {value}. Expected number of seconds when no format specified.") + raise ValueError( + f"Invalid expires_in value: {value}. Expected number of seconds when no format specified." + ) @property def token_expiry_is_time_of_expiration(self) -> bool: diff --git a/airbyte_cdk/utils/datetime_helpers.py b/airbyte_cdk/utils/datetime_helpers.py index 4348dab9..e2675eaf 100644 --- a/airbyte_cdk/utils/datetime_helpers.py +++ b/airbyte_cdk/utils/datetime_helpers.py @@ -63,12 +63,12 @@ def parse(dt_str: Union[str, int]) -> AirbyteDateTime: - Falls back to dateutil.parser for other string formats Always returns a timezone-aware datetime (defaults to UTC if no timezone specified). """ - if isinstance(dt_str, int) or (isinstance(dt_str, str) and dt_str.isdigit()): - timestamp = int(dt_str) - # Always treat numeric values as Unix timestamps - dt_obj = datetime.fromtimestamp(timestamp, tz=timezone.utc) - return AirbyteDateTime.from_datetime(dt_obj) try: + if isinstance(dt_str, int) or (isinstance(dt_str, str) and dt_str.isdigit()): + # Always treat numeric values as Unix timestamps + timestamp = int(dt_str) + dt_obj = datetime.fromtimestamp(timestamp, tz=timezone.utc) + return AirbyteDateTime.from_datetime(dt_obj) dt_obj = parser.parse(str(dt_str)) if dt_obj.tzinfo is None: dt_obj = dt_obj.replace(tzinfo=timezone.utc)