Skip to content

Commit

Permalink
(U2C 3) remove inline users in events (#188)
Browse files Browse the repository at this point in the history
  • Loading branch information
eli-darkly authored Dec 9, 2022
1 parent d068fdb commit 8f9c22a
Show file tree
Hide file tree
Showing 7 changed files with 21 additions and 113 deletions.
11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,16 @@ TEMP_TEST_OUTPUT=/tmp/contract-test-service.log

# TEST_HARNESS_PARAMS can be set to add -skip parameters for any contract tests that cannot yet pass
# Explanation of current skips:
# - "events/alias": preliminary removal of alias functionality before starting U2C implementation
# - We're preparing to migrate the SDK to U2C behavior, but so far we're still using the non-U2C contract
# tests (v1).
# - The non-U2C tests include alias events, which we have removed, so those tests are disabled.
# - Same for inline users in events.
# - Some custom event tests are disabled because in the v1 test suite, those require inline users.
TEST_HARNESS_PARAMS := $(TEST_HARNESS_PARAMS) \
-skip 'events/alias'
-skip 'events/alias' \
-skip 'events/user properties/inlineUsers=true' \
-skip 'events/custom events/data and metricValue' \
-skip 'events/custom events/basic properties/inline user'

# port 8000 and 9000 is already used in the CI environment because we're
# running a DynamoDB container and an SSE contract test
Expand Down
2 changes: 0 additions & 2 deletions contract-tests/client_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ def __init__(self, tag, config):
opts["private_attribute_names"] = events.get("globalPrivateAttributes", {})
if events.get("flushIntervalMs") is not None:
opts["flush_interval"] = events["flushIntervalMs"] / 1000.0
if events.get("inlineUsers") is not None:
opts["inline_users_in_events"] = events["inlineUsers"]
else:
opts["send_events"] = False

Expand Down
10 changes: 0 additions & 10 deletions ldclient/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ def __init__(self,
offline: bool=False,
user_keys_capacity: int=1000,
user_keys_flush_interval: float=300,
inline_users_in_events: bool=False,
diagnostic_opt_out: bool=False,
diagnostic_recording_interval: int=900,
wrapper_name: Optional[str]=None,
Expand Down Expand Up @@ -216,9 +215,6 @@ def __init__(self,
one time, so that duplicate user details will not be sent in analytics events.
:param user_keys_flush_interval: The interval in seconds at which the event processor will
reset its set of known user keys.
:param inline_users_in_events: Whether to include full user details in every analytics event.
By default, events will only include the user key, except for one "index" event that provides the
full details for the user.
:param feature_requester_class: A factory for a FeatureRequester implementation taking the sdk key and config
:param event_processor_class: A factory for an EventProcessor implementation taking the config
:param update_processor_class: A factory for an UpdateProcessor implementation taking the sdk key,
Expand Down Expand Up @@ -264,7 +260,6 @@ def __init__(self,
self.__offline = offline
self.__user_keys_capacity = user_keys_capacity
self.__user_keys_flush_interval = user_keys_flush_interval
self.__inline_users_in_events = inline_users_in_events
self.__diagnostic_opt_out = diagnostic_opt_out
self.__diagnostic_recording_interval = max(diagnostic_recording_interval, 60)
self.__wrapper_name = wrapper_name
Expand Down Expand Up @@ -298,7 +293,6 @@ def copy_with_new_sdk_key(self, new_sdk_key: str) -> 'Config':
offline=self.__offline,
user_keys_capacity=self.__user_keys_capacity,
user_keys_flush_interval=self.__user_keys_flush_interval,
inline_users_in_events=self.__inline_users_in_events,
diagnostic_opt_out=self.__diagnostic_opt_out,
diagnostic_recording_interval=self.__diagnostic_recording_interval,
wrapper_name=self.__wrapper_name,
Expand Down Expand Up @@ -410,10 +404,6 @@ def user_keys_capacity(self) -> int:
def user_keys_flush_interval(self) -> float:
return self.__user_keys_flush_interval

@property
def inline_users_in_events(self) -> bool:
return self.__inline_users_in_events

@property
def diagnostic_opt_out(self) -> bool:
return self.__diagnostic_opt_out
Expand Down
1 change: 0 additions & 1 deletion ldclient/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ def _create_diagnostic_config_object(config):
'pollingIntervalMillis': config.poll_interval * 1000,
'userKeysCapacity': config.user_keys_capacity,
'userKeysFlushIntervalMillis': config.user_keys_flush_interval * 1000,
'inlineUsersInEvents': config.inline_users_in_events,
'diagnosticRecordingIntervalMillis': config.diagnostic_recording_interval * 1000,
'dataStoreType': _get_component_type_name(config.feature_store, config, 'memory')}

Expand Down
23 changes: 9 additions & 14 deletions ldclient/event_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@

class EventOutputFormatter:
def __init__(self, config):
self._inline_users = config.inline_users_in_events
self._user_filter = UserFilter(config)

def make_output_events(self, events, summary):
Expand All @@ -59,7 +58,7 @@ def make_output_event(self, e):
}
if 'prereqOf' in e:
out['prereqOf'] = e.get('prereqOf')
if self._inline_users or is_debug:
if is_debug:
out['user'] = self._process_user(e)
else:
out['userKey'] = self._get_userkey(e)
Expand All @@ -81,10 +80,7 @@ def make_output_event(self, e):
'creationDate': e['creationDate'],
'key': e['key']
}
if self._inline_users:
out['user'] = self._process_user(e)
else:
out['userKey'] = self._get_userkey(e)
out['userKey'] = self._get_userkey(e)
if e.get('data') is not None:
out['data'] = e['data']
if e.get('metricValue') is not None:
Expand Down Expand Up @@ -310,14 +306,13 @@ def _process_event(self, event):

# For each user we haven't seen before, we add an index event - unless this is already
# an identify event for that user.
if not (add_full_event and self._config.inline_users_in_events):
user = event.get('user')
if user and 'key' in user:
is_index_event = event['kind'] == 'identify'
already_seen = self.notice_user(user)
add_index_event = not is_index_event and not already_seen
if not is_index_event and already_seen:
self._deduplicated_users += 1
user = event.get('user')
if user and 'key' in user:
is_identify_event = event['kind'] == 'identify'
already_seen = self.notice_user(user)
add_index_event = not is_identify_event and not already_seen
if not is_identify_event and already_seen:
self._deduplicated_users += 1

if add_index_event:
ie = { 'kind': 'index', 'creationDate': event['creationDate'], 'user': user }
Expand Down
8 changes: 3 additions & 5 deletions testing/test_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_create_diagnostic_config_defaults():
test_config = Config("SDK_KEY")
diag_config = _create_diagnostic_config_object(test_config)

assert len(diag_config) == 17
assert len(diag_config) == 16
assert diag_config['customBaseURI'] is False
assert diag_config['customEventsURI'] is False
assert diag_config['customStreamURI'] is False
Expand All @@ -57,7 +57,6 @@ def test_create_diagnostic_config_defaults():
assert diag_config['pollingIntervalMillis'] == 30000
assert diag_config['userKeysCapacity'] == 1000
assert diag_config['userKeysFlushIntervalMillis'] == 300000
assert diag_config['inlineUsersInEvents'] is False
assert diag_config['diagnosticRecordingIntervalMillis'] == 900000
assert diag_config['dataStoreType'] == 'memory'

Expand All @@ -67,10 +66,10 @@ def test_create_diagnostic_config_custom():
events_max_pending=10, flush_interval=1, stream_uri='https://test.com',
stream=False, poll_interval=60, use_ldd=True, feature_store=test_store,
all_attributes_private=True, user_keys_capacity=10, user_keys_flush_interval=60,
inline_users_in_events=True, http=HTTPConfig(http_proxy = 'proxy', read_timeout=1, connect_timeout=1), diagnostic_recording_interval=60)
http=HTTPConfig(http_proxy = 'proxy', read_timeout=1, connect_timeout=1), diagnostic_recording_interval=60)
diag_config = _create_diagnostic_config_object(test_config)

assert len(diag_config) == 17
assert len(diag_config) == 16
assert diag_config['customBaseURI'] is True
assert diag_config['customEventsURI'] is True
assert diag_config['customStreamURI'] is True
Expand All @@ -85,7 +84,6 @@ def test_create_diagnostic_config_custom():
assert diag_config['pollingIntervalMillis'] == 60000
assert diag_config['userKeysCapacity'] == 10
assert diag_config['userKeysFlushIntervalMillis'] == 60000
assert diag_config['inlineUsersInEvents'] is True
assert diag_config['diagnosticRecordingIntervalMillis'] == 60000
assert diag_config['dataStoreType'] == 'MyFavoriteStore'

Expand Down
79 changes: 0 additions & 79 deletions testing/test_event_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,58 +160,6 @@ def test_user_attrs_are_stringified_in_index_event():
check_feature_event(output[1], e, False, None, None)
check_summary_event(output[2])

def test_feature_event_can_contain_inline_user():
with DefaultTestProcessor(inline_users_in_events = True) as ep:
e = {
'kind': 'feature', 'key': 'flagkey', 'version': 11, 'user': user,
'variation': 1, 'value': 'value', 'default': 'default', 'trackEvents': True
}
ep.send_event(e)

output = flush_and_get_events(ep)
assert len(output) == 2
check_feature_event(output[0], e, False, user, None)
check_summary_event(output[1])

def test_user_is_filtered_in_feature_event():
with DefaultTestProcessor(inline_users_in_events = True, all_attributes_private = True) as ep:
e = {
'kind': 'feature', 'key': 'flagkey', 'version': 11, 'user': user,
'variation': 1, 'value': 'value', 'default': 'default', 'trackEvents': True
}
ep.send_event(e)

output = flush_and_get_events(ep)
assert len(output) == 2
check_feature_event(output[0], e, False, filtered_user, None)
check_summary_event(output[1])

def test_user_attrs_are_stringified_in_feature_event():
with DefaultTestProcessor(inline_users_in_events = True) as ep:
e = {
'kind': 'feature', 'key': 'flagkey', 'version': 11, 'user': numeric_user,
'variation': 1, 'value': 'value', 'default': 'default', 'trackEvents': True
}
ep.send_event(e)

output = flush_and_get_events(ep)
assert len(output) == 2
check_feature_event(output[0], e, False, stringified_numeric_user, None)
check_summary_event(output[1])

def test_index_event_is_still_generated_if_inline_users_is_true_but_feature_event_is_not_tracked():
with DefaultTestProcessor(inline_users_in_events = True) as ep:
e = {
'kind': 'feature', 'key': 'flagkey', 'version': 11, 'user': user,
'variation': 1, 'value': 'value', 'default': 'default', 'trackEvents': False
}
ep.send_event(e)

output = flush_and_get_events(ep)
assert len(output) == 2
check_index_event(output[0], e, user)
check_summary_event(output[1])

def test_two_events_for_same_user_only_produce_one_index_event():
with DefaultTestProcessor(user_keys_flush_interval = 300) as ep:
e0 = {
Expand Down Expand Up @@ -419,33 +367,6 @@ def test_custom_event_is_queued_with_user():
check_index_event(output[0], e, user)
check_custom_event(output[1], e, None)

def test_custom_event_can_contain_inline_user():
with DefaultTestProcessor(inline_users_in_events = True) as ep:
e = { 'kind': 'custom', 'key': 'eventkey', 'user': user, 'data': { 'thing': 'stuff '} }
ep.send_event(e)

output = flush_and_get_events(ep)
assert len(output) == 1
check_custom_event(output[0], e, user)

def test_user_is_filtered_in_custom_event():
with DefaultTestProcessor(inline_users_in_events = True, all_attributes_private = True) as ep:
e = { 'kind': 'custom', 'key': 'eventkey', 'user': user, 'data': { 'thing': 'stuff '} }
ep.send_event(e)

output = flush_and_get_events(ep)
assert len(output) == 1
check_custom_event(output[0], e, filtered_user)

def test_user_attrs_are_stringified_in_custom_event():
with DefaultTestProcessor(inline_users_in_events = True) as ep:
e = { 'kind': 'custom', 'key': 'eventkey', 'user': numeric_user, 'data': { 'thing': 'stuff '} }
ep.send_event(e)

output = flush_and_get_events(ep)
assert len(output) == 1
check_custom_event(output[0], e, stringified_numeric_user)

def test_nothing_is_sent_if_there_are_no_events():
with DefaultTestProcessor() as ep:
ep.flush()
Expand Down

0 comments on commit 8f9c22a

Please sign in to comment.