diff --git a/splitio/client/client.py b/splitio/client/client.py index 894503fb..27b8e5c5 100644 --- a/splitio/client/client.py +++ b/splitio/client/client.py @@ -366,7 +366,7 @@ def _record_stats(self, impressions, start, operation): self._logger.error('Error recording impressions and metrics') self._logger.debug('Error: ', exc_info=True) - def track(self, key, traffic_type, event_type, value=None, properties=None): + def track(self, key, traffic_type, event_type, value=None, properties=None, timestamp=None): # pylint: disable=too-many-arguments """ Track an event. @@ -380,6 +380,9 @@ def track(self, key, traffic_type, event_type, value=None, properties=None): :type value: Number :param properties: (Optional) properties associated to the event :type properties: dict + :param timestamp: (Optional) Represents the time in milliseconds since epoch that the event + occurred + :type properties: int :return: Whether the event was created or not. :rtype: bool @@ -399,17 +402,22 @@ def track(self, key, traffic_type, event_type, value=None, properties=None): value = input_validator.validate_value(value) valid, properties, size = input_validator.valid_properties(properties) + timestamp = input_validator.validate_timestamp(timestamp) + # pylint: disable=too-many-boolean-expressions if key is None or event_type is None or traffic_type is None or value is False \ - or valid is False: + or valid is False or timestamp is False: return False + if timestamp is None: + timestamp = int(time.time()*1000) + event = Event( key=key, traffic_type_name=traffic_type, event_type_id=event_type, value=value, - timestamp=int(time.time()*1000), + timestamp=timestamp, properties=properties, ) return self._events_storage.put([EventWrapper( diff --git a/splitio/client/input_validator.py b/splitio/client/input_validator.py index 214a7f95..88082969 100644 --- a/splitio/client/input_validator.py +++ b/splitio/client/input_validator.py @@ -545,3 +545,19 @@ def valid_properties(properties): _LOGGER.warning('Event has more than 300 properties. Some of them will be trimmed' + ' when processed') return True, valid_properties if len(valid_properties) else None, size + +def validate_timestamp(timestamp): + """ + Check if timestamp is valid for track. + + :param timestamp: timestamp to be checked + :type timestamp: int + :return: timestamp + :rtype: int|None + """ + if timestamp is None: + return None + if not isinstance(timestamp, int): + _LOGGER.error('track: timestamp must be an integer.') + return False + return timestamp diff --git a/tests/client/test_input_validator.py b/tests/client/test_input_validator.py index e02961d7..42dac040 100644 --- a/tests/client/test_input_validator.py +++ b/tests/client/test_input_validator.py @@ -765,6 +765,21 @@ def test_track(self, mocker): mocker.call("The maximum size allowed for the properties is 32768 bytes. Current one is 32952 bytes. Event not queued") ] + client._logger.reset_mock() + assert client.track("some_key", "traffic_type", "event_type", 1, None, None) is True + assert client._logger.error.mock_calls == [] + + client._logger.reset_mock() + assert client.track("some_key", "traffic_type", "event_type", 1, None, 1573936400000) is True + assert client._logger.error.mock_calls == [] + + # Test track with invalid timestamp + client._logger.reset_mock() + assert client.track("some_key", "traffic_type", "event_type", 1, None, "invalid_timestamp") is False + assert client._logger.error.mock_calls == [ + mocker.call("track: timestamp must be an integer.") + ] + def test_get_treatments(self, mocker): """Test getTreatments() method.""" split_mock = mocker.Mock(spec=Split)