Skip to content

Commit

Permalink
feat: add missing fields to register_feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
figueredo committed Jul 18, 2024
1 parent 7552141 commit 06270b2
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 4 deletions.
14 changes: 13 additions & 1 deletion incognia/api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import datetime as dt
from typing import Optional, List

from .datetime_util import total_milliseconds_since_epoch
from .datetime_util import total_milliseconds_since_epoch, has_timezone
from .endpoints import Endpoints
from .exceptions import IncogniaHTTPError, IncogniaError
from .json_util import encode
Expand Down Expand Up @@ -51,6 +51,8 @@ def register_new_signup(self,
def register_feedback(self,
event: str,
timestamp: dt.datetime = None,
occurred_at: dt.datetime = None,
expires_at: dt.datetime = None,
external_id: Optional[str] = None,
login_id: Optional[str] = None,
payment_id: Optional[str] = None,
Expand All @@ -60,6 +62,12 @@ def register_feedback(self,
session_token: Optional[str] = None) -> None:
if not event:
raise IncogniaError('event is required.')
if timestamp is not None and not has_timezone(timestamp):
raise IncogniaError('timestamp must have timezone')
if occurred_at is not None and not has_timezone(occurred_at):
raise IncogniaError('occurred_at must have timezone')
if expires_at is not None and not has_timezone(expires_at):
raise IncogniaError('expires_at must have timezone')

try:
headers = self.__get_authorization_header()
Expand All @@ -76,6 +84,10 @@ def register_feedback(self,
}
if timestamp is not None:
body['timestamp'] = total_milliseconds_since_epoch(timestamp)
if occurred_at is not None:
body['occurred_at'] = occurred_at.isoformat()
if expires_at is not None:
body['expires_at'] = expires_at.isoformat()
data = encode(body)
return self.__request.post(Endpoints.FEEDBACKS, headers=headers, data=data)

Expand Down
6 changes: 5 additions & 1 deletion incognia/datetime_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@


def total_milliseconds_since_epoch(t: dt.datetime) -> int:
return int((t - dt.datetime.utcfromtimestamp(0)).total_seconds() * 1000.0)
return int((t - dt.datetime.fromtimestamp(0, dt.timezone.utc)).total_seconds() * 1000.0)


def has_timezone(d: dt.datetime) -> bool:
return d.tzinfo is not None and d.tzinfo.utcoffset(d) is not None
52 changes: 50 additions & 2 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ class TestIncogniaAPI(TestCase):
CLIENT_ERROR_CODE: Final[int] = 400
VALID_EVENT_FEEDBACK_TYPE: Final[str] = 'valid_event_feedback_type'
INVALID_EVENT_FEEDBACK_TYPE: Final[str] = 'invalid_event_feedback_type'
TIMESTAMP: Final[dt.datetime] = dt.datetime.now()
TIMESTAMP: Final[dt.datetime] = dt.datetime.now(dt.timezone.utc)
TIMESTAMP_WITHOUT_TIMEZONE: Final[dt.datetime] = dt.datetime.now()
LOGIN_ID: Final[str] = 'ANY_LOGIN_ID'
PAYMENT_ID: Final[str] = 'ANY_PAYMENT_ID'
REGISTER_VALID_FEEDBACK_DATA: Final[bytes] = encode({
Expand All @@ -86,7 +87,10 @@ class TestIncogniaAPI(TestCase):
'account_id': f'{ACCOUNT_ID}',
'installation_id': f'{INSTALLATION_ID}',
'session_token': f'{SESSION_TOKEN}',
'timestamp': int((TIMESTAMP - dt.datetime.utcfromtimestamp(0)).total_seconds() * 1000.0)
'timestamp': int((
TIMESTAMP - dt.datetime.fromtimestamp(0, dt.timezone.utc)).total_seconds() * 1000.0),
'occurred_at': TIMESTAMP.isoformat(),
'expires_at': TIMESTAMP.isoformat(),
})
REGISTER_INVALID_FEEDBACK_DATA: Final[bytes] = encode({
'event': f'{INVALID_EVENT_FEEDBACK_TYPE}'
Expand Down Expand Up @@ -211,6 +215,8 @@ def test_register_feedback_when_all_fields_are_valid_should_work(

api.register_feedback(self.VALID_EVENT_FEEDBACK_TYPE,
timestamp=self.TIMESTAMP,
occurred_at=self.TIMESTAMP,
expires_at=self.TIMESTAMP,
external_id=self.EXTERNAL_ID,
login_id=self.LOGIN_ID,
payment_id=self.PAYMENT_ID,
Expand All @@ -235,6 +241,48 @@ def test_register_feedback_when_event_is_empty_should_raise_an_IncogniaError(
mock_token_manager_get.assert_not_called()
mock_base_request_post.assert_not_called()

@patch.object(BaseRequest, 'post')
@patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
def test_register_feedback_when_timestamp_does_not_have_timezone_should_raise_IncogniaError(
self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)

self.assertRaises(IncogniaError,
api.register_feedback,
event=self.VALID_EVENT_FEEDBACK_TYPE,
timestamp=self.TIMESTAMP_WITHOUT_TIMEZONE)

mock_token_manager_get.assert_not_called()
mock_base_request_post.assert_not_called()

@patch.object(BaseRequest, 'post')
@patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
def test_register_feedback_when_occurred_at_does_not_have_timezone_should_raise_IncogniaError(
self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)

self.assertRaises(IncogniaError,
api.register_feedback,
event=self.VALID_EVENT_FEEDBACK_TYPE,
occurred_at=self.TIMESTAMP_WITHOUT_TIMEZONE)

mock_token_manager_get.assert_not_called()
mock_base_request_post.assert_not_called()

@patch.object(BaseRequest, 'post')
@patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
def test_register_feedback_when_expires_at_does_not_have_timezone_should_raise_IncogniaError(
self, mock_token_manager_get: Mock, mock_base_request_post: Mock):
api = IncogniaAPI(self.CLIENT_ID, self.CLIENT_SECRET)

self.assertRaises(IncogniaError,
api.register_feedback,
event=self.VALID_EVENT_FEEDBACK_TYPE,
expires_at=self.TIMESTAMP_WITHOUT_TIMEZONE)

mock_token_manager_get.assert_not_called()
mock_base_request_post.assert_not_called()

@patch.object(BaseRequest, 'post')
@patch.object(TokenManager, 'get', return_value=TOKEN_VALUES)
def test_register_feedback_when_required_fields_are_invalid_should_raise_an_IncogniaHTTPError(
Expand Down

0 comments on commit 06270b2

Please sign in to comment.