Skip to content

Commit

Permalink
Merge pull request #2 from kharigardner/hotfix/timestamp_serialization
Browse files Browse the repository at this point in the history
Hotfix/timestamp serialization
  • Loading branch information
kharigardner authored Mar 4, 2024
2 parents 3730a21 + 9e24101 commit bba2a2c
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 19 deletions.
37 changes: 32 additions & 5 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion pyfivetran/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from __future__ import annotations # for | union syntax

from logging import getLogger
from dateutil.parser import isoparse
import datetime as dt
import sys


logger = getLogger(__name__)

Expand All @@ -13,7 +16,9 @@ def deserialize_timestamp(dt_str: str) -> dt.datetime:
:param dt_str: The timestamp to deserialize
:return: The deserialized timestamp
"""
return dt.datetime.strptime(dt_str, "%Y-%m-%dT%H:%M:%SZ")
if sys.version_info.minor < 11:
return isoparse(dt_str)
return dt.datetime.fromisoformat(dt_str)


def serialize_timezone(tz: str | dt.tzinfo) -> int:
Expand Down
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyfivetran"
version = "0.1.2"
version = "0.1.3"
description = "Pythonic interface to the fivetran API"
authors = ["Khari Gardner <kharigardner@protonmail.com>"]
license = "MIT"
Expand All @@ -10,6 +10,8 @@ readme = "readme.md"
python = "^3.8, <3.12"
httpx = "^0.25.1"
lazy = "^1.6"
pytz = "^2024.1"
python-dateutil = "^2.9.0.post0"


[tool.poetry.group.dev.dependencies]
Expand Down
6 changes: 4 additions & 2 deletions tests/endpoints/test_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from datetime import datetime
from unittest.mock import MagicMock

import pytz

from pyfivetran.shed import ApiError
from pyfivetran.endpoints.group import Group, GroupEndpoint, BASE_API_URL, API_VERSION, GeneralApiResponse

Expand Down Expand Up @@ -69,7 +71,7 @@ def test__from_dict(self, group_endpoint_mock):
group = Group._from_dict(group_endpoint_mock, group_dict)
assert group.fivetran_id == '123'
assert group.name == 'Test Group'
assert group.created_at == datetime(2022, 1, 1)
assert group.created_at == datetime(2022, 1, 1, tzinfo=pytz.utc)
assert group._raw == group_dict

def test_delete(self, group_endpoint_mock):
Expand Down Expand Up @@ -188,7 +190,7 @@ def test_create_group_with_valid_name(self, mocker: MockerFixture):
group = group_endpoint.create_group(group_name)
assert group.name == group_name
assert group.fivetran_id == '123'
assert group.created_at == datetime(2022, 1, 1, 0, 0, 0)
assert group.created_at == datetime(2022, 1, 1, 0, 0, 0, tzinfo=pytz.utc)

# can list groups with valid limit (fixed)
def test_list_groups_with_valid_limit_fixed(self, mocker):
Expand Down
13 changes: 7 additions & 6 deletions tests/endpoints/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pytest

import pytz
from pyfivetran.endpoints.users import UserEndpoint, User, Client, datetime


Expand All @@ -28,8 +29,8 @@ def test_create_user_with_required_fields(self):
assert user.verified == True
assert user.role == "admin"
assert user.active == True
assert user.created_at == datetime(2022, 1, 1, 0, 0, 0)
assert user.logged_in_at == datetime(2022, 1, 1, 0, 0, 0)
assert user.created_at == datetime(2022, 1, 1, 0, 0, 0, tzinfo=pytz.utc)
assert user.logged_in_at == datetime(2022, 1, 1, 0, 0, 0, tzinfo=pytz.utc)
assert user.family_name == None
assert user.given_name == None
assert user.invited == None
Expand Down Expand Up @@ -81,8 +82,8 @@ def test_invite_user_with_optional_fields(self, mocker):
assert user.verified == True
assert user.role == "admin"
assert user.active == True
assert user.created_at == datetime(2022, 1, 1, 0, 0, 0)
assert user.logged_in_at == datetime(2022, 1, 1, 0, 0, 0)
assert user.created_at == datetime(2022, 1, 1, 0, 0, 0, tzinfo=pytz.utc)
assert user.logged_in_at == datetime(2022, 1, 1, 0, 0, 0, tzinfo=pytz.utc)
assert user.family_name == "Doe"
assert user.given_name == "John"
assert user.invited == True
Expand Down Expand Up @@ -111,8 +112,8 @@ def test_create_user_without_required_field(self):
assert user.verified == True
assert user.role == "admin"
assert user.active == True
assert user.created_at == datetime(2022, 1, 1, 0, 0, 0)
assert user.logged_in_at == datetime(2022, 1, 1, 0, 0, 0)
assert user.created_at == datetime(2022, 1, 1, 0, 0, 0, tzinfo=pytz.utc)
assert user.logged_in_at == datetime(2022, 1, 1, 0, 0, 0, tzinfo=pytz.utc)
assert user.family_name == None
assert user.given_name == None
assert user.invited == None
Expand Down
10 changes: 6 additions & 4 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest

import datetime
import pytz

from pyfivetran.utils import deserialize_timestamp, serialize_timezone

Expand All @@ -9,19 +10,19 @@ class TestDeserializeTimestamp:
# Returns a datetime object when given a valid timestamp string in the format 'YYYY-MM-DDTHH:MM:SSZ'
def test_valid_timestamp(self):
timestamp = '2019-08-24T14:15:22Z'
expected = datetime.datetime(2019, 8, 24, 14, 15, 22)
expected = datetime.datetime(2019, 8, 24, 14, 15, 22, tzinfo=pytz.UTC)
assert deserialize_timestamp(timestamp) == expected

# Returns a datetime object with the correct year, month, day, hour, minute, and second values
def test_correct_values(self):
timestamp = '2022-12-31T23:59:59Z'
expected = datetime.datetime(2022, 12, 31, 23, 59, 59)
expected = datetime.datetime(2022, 12, 31, 23, 59, 59, tzinfo=pytz.UTC)
assert deserialize_timestamp(timestamp) == expected

# Handles timestamps with single-digit months, days, hours, minutes, and seconds
def test_single_digit_values(self):
timestamp = '2022-01-01T01:02:03Z'
expected = datetime.datetime(2022, 1, 1, 1, 2, 3)
expected = datetime.datetime(2022, 1, 1, 1, 2, 3, tzinfo=pytz.UTC)
assert deserialize_timestamp(timestamp) == expected

# Raises a ValueError when given an empty string
Expand All @@ -32,7 +33,8 @@ def test_empty_string(self):

# Raises a ValueError when given a timestamp string with an invalid format
def test_invalid_format(self):
timestamp = '2022-01-01T01:02:03'
# non iso format
timestamp = '2020/12/31 23:59:59Z'
with pytest.raises(ValueError):
deserialize_timestamp(timestamp)

Expand Down

0 comments on commit bba2a2c

Please sign in to comment.