Handle more kinds of bad data in segment events. (#25506)

This commit is contained in:
brianhw
2020-11-03 15:04:05 -05:00
committed by GitHub
parent 4a0e4aa16d
commit 89169443ad
3 changed files with 60 additions and 27 deletions

View File

@@ -123,16 +123,16 @@ def track_segmentio_event(request): # pylint: disable=too-many-statements
full_segment_event = request.json
# We mostly care about the properties
segment_properties = full_segment_event.get('properties', {})
segment_properties = _get_dict_value_with_default(full_segment_event, 'properties', {})
# Start with the context provided by Segment in the "client" field if it exists
# We should tightly control which fields actually get included in the event emitted.
segment_context = full_segment_event.get('context', {})
segment_context = _get_dict_value_with_default(full_segment_event, 'context', {})
# Build up the event context by parsing fields out of the event received from Segment
context = {}
library_name = segment_context.get('library', {}).get('name')
library_name = _get_dict_value_with_default(segment_context, 'library', {}).get('name')
source_map = getattr(settings, 'TRACKING_SEGMENTIO_SOURCE_MAP', {})
event_source = source_map.get(library_name)
if not event_source:
@@ -268,3 +268,12 @@ def _get_segmentio_event_name(event_properties):
def parse_iso8601_timestamp(timestamp):
"""Parse a particular type of ISO8601 formatted timestamp"""
return parser.parse(timestamp)
def _get_dict_value_with_default(dict_object, key, default):
"""
Returns default if the dict doesn't have the key or if the value is Falsey.
Otherwise, returns the dict's value for the key.
"""
value = dict_object.get(key, None)
return value if value else default

View File

@@ -60,6 +60,14 @@ class SegmentIOTrackingTestCaseBase(EventTrackingTestCase):
)
segmentio.track_segmentio_event(request)
def post_modified_segmentio_event(self, event):
"""Post an externally-defined fake Segment event to the view that processes it"""
request = self.create_request(
data=json.dumps(event),
content_type='application/json'
)
segmentio.track_segmentio_event(request)
def create_segmentio_event(self, **kwargs):
"""Populate a fake Segment event with data of interest"""
action = kwargs.get('action', 'Track')

View File

@@ -68,10 +68,46 @@ class SegmentIOTrackingTestCase(SegmentIOTrackingTestCaseBase):
self.post_segmentio_event(action=action)
self.assert_no_events_emitted()
def test_segmentio_ignore_missing_context_entry(self):
sample_event_raw = self.create_segmentio_event()
del sample_event_raw['context']
self.post_modified_segmentio_event(sample_event_raw)
self.assert_no_events_emitted()
def test_segmentio_ignore_null_context_entry(self):
sample_event_raw = self.create_segmentio_event()
sample_event_raw['context'] = None
self.post_modified_segmentio_event(sample_event_raw)
self.assert_no_events_emitted()
def test_segmentio_ignore_missing_library_entry(self):
sample_event_raw = self.create_segmentio_event()
del sample_event_raw['context']['library']
self.post_modified_segmentio_event(sample_event_raw)
self.assert_no_events_emitted()
def test_segmentio_ignore_null_library_entry(self):
sample_event_raw = self.create_segmentio_event()
sample_event_raw['context']['library'] = None
self.post_modified_segmentio_event(sample_event_raw)
self.assert_no_events_emitted()
def test_segmentio_ignore_unknown_libraries(self):
self.post_segmentio_event(library_name='foo')
self.assert_no_events_emitted()
@expect_failure_with_message(segmentio.ERROR_MISSING_NAME)
def test_segmentio_ignore_missing_properties_entry(self):
sample_event_raw = self.create_segmentio_event()
del sample_event_raw['properties']
self.post_modified_segmentio_event(sample_event_raw)
@expect_failure_with_message(segmentio.ERROR_MISSING_NAME)
def test_segmentio_ignore_null_properties_entry(self):
sample_event_raw = self.create_segmentio_event()
sample_event_raw['properties'] = None
self.post_modified_segmentio_event(sample_event_raw)
@expect_failure_with_message(segmentio.ERROR_USER_NOT_EXIST)
def test_no_user_for_user_id(self):
self.post_segmentio_event(user_id=40)
@@ -148,43 +184,23 @@ class SegmentIOTrackingTestCase(SegmentIOTrackingTestCaseBase):
def test_missing_name(self):
sample_event_raw = self.create_segmentio_event()
del sample_event_raw['properties']['name']
request = self.create_request(
data=json.dumps(sample_event_raw),
content_type='application/json'
)
segmentio.track_segmentio_event(request)
self.post_modified_segmentio_event(sample_event_raw)
@expect_failure_with_message(segmentio.ERROR_MISSING_DATA)
def test_missing_data(self):
sample_event_raw = self.create_segmentio_event()
del sample_event_raw['properties']['data']
request = self.create_request(
data=json.dumps(sample_event_raw),
content_type='application/json'
)
segmentio.track_segmentio_event(request)
self.post_modified_segmentio_event(sample_event_raw)
@expect_failure_with_message(segmentio.ERROR_MISSING_TIMESTAMP)
def test_missing_timestamp(self):
sample_event_raw = self.create_event_without_fields('timestamp')
request = self.create_request(
data=json.dumps(sample_event_raw),
content_type='application/json'
)
segmentio.track_segmentio_event(request)
self.post_modified_segmentio_event(sample_event_raw)
@expect_failure_with_message(segmentio.ERROR_MISSING_RECEIVED_AT)
def test_missing_received_at(self):
sample_event_raw = self.create_event_without_fields('receivedAt')
request = self.create_request(
data=json.dumps(sample_event_raw),
content_type='application/json'
)
segmentio.track_segmentio_event(request)
self.post_modified_segmentio_event(sample_event_raw)
def create_event_without_fields(self, *fields):
"""Create a fake event and remove some fields from it"""