diff --git a/optimizely/event/event_factory.py b/optimizely/event/event_factory.py index 715b88c5..c872fb17 100644 --- a/optimizely/event/event_factory.py +++ b/optimizely/event/event_factory.py @@ -42,7 +42,10 @@ class EventFactory: to record the events via the Optimizely Events API ("https://developers.optimizely.com/x/events/api/index.html") """ - EVENT_ENDPOINT: Final = 'https://logx.optimizely.com/v1/events' + EVENT_ENDPOINTS: Final = { + 'US': 'https://logx.optimizely.com/v1/events', + 'EU': 'https://eu.logx.optimizely.com/v1/events' + } HTTP_VERB: Final = 'POST' HTTP_HEADERS: Final = {'Content-Type': 'application/json'} ACTIVATE_EVENT_KEY: Final = 'campaign_activated' @@ -97,7 +100,11 @@ def create_log_event( event_params = event_batch.get_event_params() - return log_event.LogEvent(cls.EVENT_ENDPOINT, event_params, cls.HTTP_VERB, cls.HTTP_HEADERS) + region = user_context.region or 'US' # Default to 'US' if None + region_key = str(region).upper() + endpoint = cls.EVENT_ENDPOINTS.get(region_key, cls.EVENT_ENDPOINTS['US']) + + return log_event.LogEvent(endpoint, event_params, cls.HTTP_VERB, cls.HTTP_HEADERS) @classmethod def _create_visitor(cls, event: Optional[user_event.UserEvent], logger: Logger) -> Optional[payload.Visitor]: diff --git a/optimizely/event/user_event.py b/optimizely/event/user_event.py index 68c1ee78..e257647c 100644 --- a/optimizely/event/user_event.py +++ b/optimizely/event/user_event.py @@ -99,10 +99,11 @@ def __init__( class EventContext: """ Class respresenting User Event Context. """ - def __init__(self, account_id: str, project_id: str, revision: str, anonymize_ip: bool): + def __init__(self, account_id: str, project_id: str, revision: str, anonymize_ip: bool, region: str): self.account_id = account_id self.project_id = project_id self.revision = revision self.client_name = CLIENT_NAME self.client_version = version.__version__ self.anonymize_ip = anonymize_ip + self.region = region or 'US' diff --git a/optimizely/event/user_event_factory.py b/optimizely/event/user_event_factory.py index b41be39a..7a77720f 100644 --- a/optimizely/event/user_event_factory.py +++ b/optimizely/event/user_event_factory.py @@ -77,7 +77,11 @@ def create_impression_event( variation = project_config.get_variation_from_id_by_experiment_id(experiment_id, variation_id) event_context = user_event.EventContext( - project_config.account_id, project_config.project_id, project_config.revision, project_config.anonymize_ip, + project_config.account_id, + project_config.project_id, + project_config.revision, + project_config.anonymize_ip, + project_config.region ) return user_event.ImpressionEvent( @@ -117,7 +121,11 @@ def create_conversion_event( """ event_context = user_event.EventContext( - project_config.account_id, project_config.project_id, project_config.revision, project_config.anonymize_ip, + project_config.account_id, + project_config.project_id, + project_config.revision, + project_config.anonymize_ip, + project_config.region ) return user_event.ConversionEvent( diff --git a/optimizely/event_builder.py b/optimizely/event_builder.py index ecabf14c..90678830 100644 --- a/optimizely/event_builder.py +++ b/optimizely/event_builder.py @@ -54,7 +54,10 @@ class EventBuilder: """ Class which encapsulates methods to build events for tracking impressions and conversions using the new V3 event API (batch). """ - EVENTS_URL: Final = 'https://logx.optimizely.com/v1/events' + EVENTS_URLS: Final = { + 'US': 'https://logx.optimizely.com/v1/events', + 'EU': 'https://eu.logx.optimizely.com/v1/events' + } HTTP_VERB: Final = 'POST' HTTP_HEADERS: Final = {'Content-Type': 'application/json'} @@ -266,7 +269,11 @@ def create_impression_event( params[self.EventParams.USERS][0][self.EventParams.SNAPSHOTS].append(impression_params) - return Event(self.EVENTS_URL, params, http_verb=self.HTTP_VERB, headers=self.HTTP_HEADERS) + region = project_config.region or 'US' + region_key = str(region).upper() + events_url = self.EVENTS_URLS.get(region_key, self.EVENTS_URLS['US']) + + return Event(events_url, params, http_verb=self.HTTP_VERB, headers=self.HTTP_HEADERS) def create_conversion_event( self, project_config: ProjectConfig, event_key: str, @@ -289,4 +296,9 @@ def create_conversion_event( conversion_params = self._get_required_params_for_conversion(project_config, event_key, event_tags) params[self.EventParams.USERS][0][self.EventParams.SNAPSHOTS].append(conversion_params) - return Event(self.EVENTS_URL, params, http_verb=self.HTTP_VERB, headers=self.HTTP_HEADERS) + + region = project_config.region or 'US' + region_key = str(region).upper() + events_url = self.EVENTS_URLS.get(region_key, self.EVENTS_URLS['US']) + + return Event(events_url, params, http_verb=self.HTTP_VERB, headers=self.HTTP_HEADERS) diff --git a/optimizely/project_config.py b/optimizely/project_config.py index f774ff8a..446d1e2f 100644 --- a/optimizely/project_config.py +++ b/optimizely/project_config.py @@ -85,6 +85,9 @@ def __init__(self, datafile: str | bytes, logger: Logger, error_handler: Any): self.host_for_odp: Optional[str] = None self.all_segments: list[str] = [] + region_value = config.get('region') + self.region: str = region_value or 'US' + # Utility maps for quick lookup self.group_id_map: dict[str, entities.Group] = self._generate_key_map(self.groups, 'id', entities.Group) self.experiment_id_map: dict[str, entities.Experiment] = self._generate_key_map( diff --git a/tests/test_config.py b/tests/test_config.py index 9ec5c761..a6e828c2 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -154,6 +154,28 @@ def test_init(self): self.assertEqual(expected_variation_key_map, self.project_config.variation_key_map) self.assertEqual(expected_variation_id_map, self.project_config.variation_id_map) + def test_region_when_no_region(self): + """ Test that region defaults to 'US' when not specified in the config. """ + config_dict = copy.deepcopy(self.config_dict_with_multiple_experiments) + opt_obj = optimizely.Optimizely(json.dumps(config_dict)) + project_config = opt_obj.config_manager.get_config() + self.assertEqual(project_config.region, 'US') + + def test_region_when_specified_in_datafile(self): + """ Test that region is set to 'US' when specified in the config. """ + config_dict_us = copy.deepcopy(self.config_dict_with_multiple_experiments) + config_dict_us['region'] = 'US' + opt_obj_us = optimizely.Optimizely(json.dumps(config_dict_us)) + project_config_us = opt_obj_us.config_manager.get_config() + self.assertEqual(project_config_us.region, 'US') + + """ Test that region is set to 'EU' when specified in the config. """ + config_dict_eu = copy.deepcopy(self.config_dict_with_multiple_experiments) + config_dict_eu['region'] = 'EU' + opt_obj_eu = optimizely.Optimizely(json.dumps(config_dict_eu)) + project_config_eu = opt_obj_eu.config_manager.get_config() + self.assertEqual(project_config_eu.region, 'EU') + def test_cmab_field_population(self): """ Test that the cmab field is populated correctly in experiments.""" diff --git a/tests/test_event_builder.py b/tests/test_event_builder.py index fb4d7a0d..237e17a4 100644 --- a/tests/test_event_builder.py +++ b/tests/test_event_builder.py @@ -100,7 +100,64 @@ def test_create_impression_event(self): ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), + expected_params, + event_builder.EventBuilder.HTTP_VERB, + event_builder.EventBuilder.HTTP_HEADERS, + ) + + def test_create_impression_event_with_EU(self): + """ Test that create_impression_event creates Event object with right params and EU region. """ + + expected_params = { + 'account_id': '12001', + 'project_id': '111001', + 'visitors': [ + { + 'visitor_id': 'test_user', + 'attributes': [], + 'snapshots': [ + { + 'decisions': [ + {'variation_id': '111129', 'experiment_id': '111127', 'campaign_id': '111182'} + ], + 'events': [ + { + 'timestamp': 42123, + 'entity_id': '111182', + 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + 'key': 'campaign_activated', + } + ], + } + ], + } + ], + 'client_name': 'python-sdk', + 'client_version': version.__version__, + 'enrich_decisions': True, + 'anonymize_ip': False, + 'revision': '42', + } + + self.project_config.region = 'EU' + + with mock.patch.object(self.project_config, 'region', new='EU'), mock.patch( + 'time.time', return_value=42.123 + ), mock.patch( + 'optimizely.bucketer.Bucketer._generate_bucket_value', return_value=5042 + ), mock.patch('uuid.uuid4', return_value='a68cf1ad-0393-4e18-af87-efe8f01a7c9c'): + self.project_config.region = 'EU' + event_obj = self.event_builder.create_impression_event( + self.project_config, + self.project_config.get_experiment_from_key('test_experiment'), + '111129', + 'test_user', + None, + ) + self._validate_event_object( + event_obj, + event_builder.EventBuilder.EVENTS_URLS.get('EU'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -155,7 +212,7 @@ def test_create_impression_event__with_attributes(self): ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -204,11 +261,11 @@ def test_create_impression_event_when_attribute_is_not_in_datafile(self): self.project_config.get_experiment_from_key('test_experiment'), '111129', 'test_user', - {'do_you_know_me': 'test_value'}, + {'do_you_know_me': 'test_value'} ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -275,12 +332,12 @@ def side_effect(*args, **kwargs): self.project_config.get_experiment_from_key('test_experiment'), '111129', 'test_user', - attributes, + attributes ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -340,12 +397,12 @@ def test_create_impression_event__with_user_agent_when_bot_filtering_is_enabled( self.project_config.get_experiment_from_key('test_experiment'), '111129', 'test_user', - {'$opt_user_agent': 'Edge'}, + {'$opt_user_agent': 'Edge'} ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -404,12 +461,12 @@ def test_create_impression_event__with_empty_attributes_when_bot_filtering_is_en self.project_config.get_experiment_from_key('test_experiment'), '111129', 'test_user', - None, + None ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -474,12 +531,12 @@ def test_create_impression_event__with_user_agent_when_bot_filtering_is_disabled self.project_config.get_experiment_from_key('test_experiment'), '111129', 'test_user', - {'$opt_user_agent': 'Chrome'}, + {'$opt_user_agent': 'Chrome'} ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -525,7 +582,103 @@ def test_create_conversion_event(self): ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), + expected_params, + event_builder.EventBuilder.HTTP_VERB, + event_builder.EventBuilder.HTTP_HEADERS, + ) + + def test_create_conversion_event_with_eu(self): + """ Test that create_conversion_event creates Event object + with right params when no attributes are provided. """ + + expected_params = { + 'account_id': '12001', + 'project_id': '111001', + 'visitors': [ + { + 'visitor_id': 'test_user', + 'attributes': [], + 'snapshots': [ + { + 'events': [ + { + 'timestamp': 42123, + 'entity_id': '111095', + 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + 'key': 'test_event', + } + ] + } + ], + } + ], + 'client_name': 'python-sdk', + 'client_version': version.__version__, + 'enrich_decisions': True, + 'anonymize_ip': False, + 'revision': '42', + } + + with mock.patch.object(self.project_config, 'region', new='EU'), mock.patch( + 'time.time', return_value=42.123 + ), mock.patch( + 'uuid.uuid4', return_value='a68cf1ad-0393-4e18-af87-efe8f01a7c9c' + ): + event_obj = self.event_builder.create_conversion_event( + self.project_config, 'test_event', 'test_user', None, None + ) + self._validate_event_object( + event_obj, + event_builder.EventBuilder.EVENTS_URLS.get('EU'), + expected_params, + event_builder.EventBuilder.HTTP_VERB, + event_builder.EventBuilder.HTTP_HEADERS, + ) + + def test_create_conversion_event_with_invalid_region(self): + """ Test that create_conversion_event creates Event object + with right params when no attributes are provided. """ + + expected_params = { + 'account_id': '12001', + 'project_id': '111001', + 'visitors': [ + { + 'visitor_id': 'test_user', + 'attributes': [], + 'snapshots': [ + { + 'events': [ + { + 'timestamp': 42123, + 'entity_id': '111095', + 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + 'key': 'test_event', + } + ] + } + ], + } + ], + 'client_name': 'python-sdk', + 'client_version': version.__version__, + 'enrich_decisions': True, + 'anonymize_ip': False, + 'revision': '42', + } + + with mock.patch.object(self.project_config, 'region', new='ZZ'), mock.patch( + 'time.time', return_value=42.123 + ), mock.patch( + 'uuid.uuid4', return_value='a68cf1ad-0393-4e18-af87-efe8f01a7c9c' + ): + event_obj = self.event_builder.create_conversion_event( + self.project_config, 'test_event', 'test_user', None, None + ) + self._validate_event_object( + event_obj, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -569,11 +722,11 @@ def test_create_conversion_event__with_attributes(self): 'uuid.uuid4', return_value='a68cf1ad-0393-4e18-af87-efe8f01a7c9c' ): event_obj = self.event_builder.create_conversion_event( - self.project_config, 'test_event', 'test_user', {'test_attribute': 'test_value'}, None, + self.project_config, 'test_event', 'test_user', {'test_attribute': 'test_value'}, None ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -626,12 +779,12 @@ def test_create_conversion_event__with_user_agent_when_bot_filtering_is_enabled( 'optimizely.project_config.ProjectConfig.get_bot_filtering_value', return_value=True, ): event_obj = self.event_builder.create_conversion_event( - self.project_config, 'test_event', 'test_user', {'$opt_user_agent': 'Edge'}, None, + self.project_config, 'test_event', 'test_user', {'$opt_user_agent': 'Edge'}, None ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -689,12 +842,12 @@ def test_create_conversion_event__with_user_agent_when_bot_filtering_is_disabled 'optimizely.project_config.ProjectConfig.get_bot_filtering_value', return_value=False, ): event_obj = self.event_builder.create_conversion_event( - self.project_config, 'test_event', 'test_user', {'$opt_user_agent': 'Chrome'}, None, + self.project_config, 'test_event', 'test_user', {'$opt_user_agent': 'Chrome'}, None ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -745,11 +898,11 @@ def test_create_conversion_event__with_event_tags(self): 'test_event', 'test_user', {'test_attribute': 'test_value'}, - {'revenue': 4200, 'value': 1.234, 'non-revenue': 'abc'}, + {'revenue': 4200, 'value': 1.234, 'non-revenue': 'abc'} ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -798,11 +951,11 @@ def test_create_conversion_event__with_invalid_event_tags(self): 'test_event', 'test_user', {'test_attribute': 'test_value'}, - {'revenue': '4200', 'value': True, 'non-revenue': 'abc'}, + {'revenue': '4200', 'value': True, 'non-revenue': 'abc'} ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, @@ -853,11 +1006,11 @@ def test_create_conversion_event__when_event_is_used_in_multiple_experiments(sel 'test_event', 'test_user', {'test_attribute': 'test_value'}, - {'revenue': 4200, 'value': 1.234, 'non-revenue': 'abc'}, + {'revenue': 4200, 'value': 1.234, 'non-revenue': 'abc'} ) self._validate_event_object( event_obj, - event_builder.EventBuilder.EVENTS_URL, + event_builder.EventBuilder.EVENTS_URLS.get('US'), expected_params, event_builder.EventBuilder.HTTP_VERB, event_builder.EventBuilder.HTTP_HEADERS, diff --git a/tests/test_event_factory.py b/tests/test_event_factory.py index 59edd7c3..6d70c713 100644 --- a/tests/test_event_factory.py +++ b/tests/test_event_factory.py @@ -119,7 +119,78 @@ def test_create_impression_event(self): log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, + ) + + def test_create_impression_event_with_eu(self): + """ Test that create_impression_event creates LogEvent object with right params and region is EU. """ + + expected_params = { + 'account_id': '12001', + 'project_id': '111001', + 'visitors': [ + { + 'visitor_id': 'test_user', + 'attributes': [], + 'snapshots': [ + { + 'decisions': [ + {'variation_id': '111129', 'experiment_id': '111127', 'campaign_id': '111182', + 'metadata': {'flag_key': '', + 'rule_key': 'rule_key', + 'rule_type': 'experiment', + 'variation_key': 'variation', + 'enabled': False}} + ], + 'events': [ + { + 'timestamp': 42123, + 'entity_id': '111182', + 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + 'key': 'campaign_activated', + } + ], + } + ], + } + ], + 'client_name': 'python-sdk', + 'client_version': version.__version__, + 'enrich_decisions': True, + 'anonymize_ip': False, + 'revision': '42', + } + + self.project_config.region = 'EU' + + with mock.patch('time.time', return_value=42.123), mock.patch( + 'uuid.uuid4', return_value='a68cf1ad-0393-4e18-af87-efe8f01a7c9c' + ): + event_obj = UserEventFactory.create_impression_event( + self.project_config, + self.project_config.get_experiment_from_key('test_experiment'), + '111129', + '', + 'rule_key', + 'experiment', + False, + 'test_user', + None, + None, + ) + + log_event = EventFactory.create_log_event(event_obj, self.logger) + + self._validate_event_object( + log_event, + EventFactory.EVENT_ENDPOINTS.get('EU'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_impression_event__with_attributes(self): @@ -184,7 +255,11 @@ def test_create_impression_event__with_attributes(self): log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_impression_event_when_attribute_is_not_in_datafile(self): @@ -247,7 +322,11 @@ def test_create_impression_event_when_attribute_is_not_in_datafile(self): log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_impression_event_calls_is_attribute_valid(self): @@ -403,7 +482,11 @@ def test_create_impression_event__with_user_agent_when_bot_filtering_is_enabled( log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_impression_event__with_empty_attributes_when_bot_filtering_is_enabled(self,): @@ -476,7 +559,11 @@ def test_create_impression_event__with_empty_attributes_when_bot_filtering_is_en log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_impression_event__with_user_agent_when_bot_filtering_is_disabled(self,): @@ -555,7 +642,11 @@ def test_create_impression_event__with_user_agent_when_bot_filtering_is_disabled log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_conversion_event(self): @@ -600,7 +691,62 @@ def test_create_conversion_event(self): log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, + ) + + def test_create_conversion_event_with_eu(self): + """ Test that create_conversion_event creates Event object + with right params when no attributes are provided and region is EU. """ + + expected_params = { + 'account_id': '12001', + 'project_id': '111001', + 'visitors': [ + { + 'visitor_id': 'test_user', + 'attributes': [], + 'snapshots': [ + { + 'events': [ + { + 'timestamp': 42123, + 'entity_id': '111095', + 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + 'key': 'test_event', + } + ] + } + ], + } + ], + 'client_name': 'python-sdk', + 'client_version': version.__version__, + 'enrich_decisions': True, + 'anonymize_ip': False, + 'revision': '42', + } + + self.project_config.region = 'EU' + + with mock.patch('time.time', return_value=42.123), mock.patch( + 'uuid.uuid4', return_value='a68cf1ad-0393-4e18-af87-efe8f01a7c9c' + ): + event_obj = UserEventFactory.create_conversion_event( + self.project_config, 'test_event', 'test_user', None, None + ) + + log_event = EventFactory.create_log_event(event_obj, self.logger) + + self._validate_event_object( + log_event, + EventFactory.EVENT_ENDPOINTS.get('EU'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_conversion_event__with_attributes(self): @@ -647,7 +793,11 @@ def test_create_conversion_event__with_attributes(self): log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_conversion_event__with_user_agent_when_bot_filtering_is_enabled(self,): @@ -703,7 +853,11 @@ def test_create_conversion_event__with_user_agent_when_bot_filtering_is_enabled( log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_conversion_event__with_user_agent_when_bot_filtering_is_disabled(self,): @@ -764,7 +918,11 @@ def test_create_conversion_event__with_user_agent_when_bot_filtering_is_disabled log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_conversion_event__with_event_tags(self): @@ -818,7 +976,11 @@ def test_create_conversion_event__with_event_tags(self): log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_conversion_event__with_invalid_event_tags(self): @@ -870,7 +1032,11 @@ def test_create_conversion_event__with_invalid_event_tags(self): log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_conversion_event__when_event_is_used_in_multiple_experiments(self): @@ -924,7 +1090,11 @@ def test_create_conversion_event__when_event_is_used_in_multiple_experiments(sel log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_impression_event_with_cmab_uuid(self): @@ -988,7 +1158,11 @@ def test_create_impression_event_with_cmab_uuid(self): log_event = EventFactory.create_log_event(event_obj, self.logger) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) def test_create_impression_event_without_cmab_uuid(self): @@ -1057,5 +1231,9 @@ def test_create_impression_event_without_cmab_uuid(self): self.assertNotIn('cmab_uuid', metadata) self._validate_event_object( - log_event, EventFactory.EVENT_ENDPOINT, expected_params, EventFactory.HTTP_VERB, EventFactory.HTTP_HEADERS, + log_event, + EventFactory.EVENT_ENDPOINTS.get('US'), + expected_params, + EventFactory.HTTP_VERB, + EventFactory.HTTP_HEADERS, ) diff --git a/tests/test_event_processor.py b/tests/test_event_processor.py index 4e45e6fc..69c6ce31 100644 --- a/tests/test_event_processor.py +++ b/tests/test_event_processor.py @@ -576,7 +576,7 @@ def __init__(self, is_updated=False): self.is_updated = is_updated def dispatch_event(self, log_event): - if log_event.http_verb == 'POST' and log_event.url == EventFactory.EVENT_ENDPOINT: + if log_event.http_verb == 'POST' and log_event.url in EventFactory.EVENT_ENDPOINTS.values(): self.is_updated = True return self.is_updated diff --git a/tests/test_user_event_factory.py b/tests/test_user_event_factory.py index 77f985d8..489ba987 100644 --- a/tests/test_user_event_factory.py +++ b/tests/test_user_event_factory.py @@ -41,6 +41,21 @@ def test_impression_event(self): self.assertEqual(experiment, impression_event.experiment) self.assertEqual(variation, impression_event.variation) self.assertEqual(user_id, impression_event.user_id) + self.assertEqual(self.project_config.region, impression_event.event_context.region) + + def test_impression_event_with_region_eu(self): + project_config = self.project_config + experiment = self.project_config.get_experiment_from_key('test_experiment') + user_id = 'test_user' + + project_config.region = 'EU' + + impression_event = UserEventFactory.create_impression_event( + project_config, experiment, '111128', '', 'rule_key', 'rule_type', True, user_id, None, None + ) + + self.assertEqual(self.project_config.region, impression_event.event_context.region) + self.assertEqual('EU', impression_event.event_context.region) def test_impression_event__with_attributes(self): project_config = self.project_config @@ -66,6 +81,7 @@ def test_impression_event__with_attributes(self): self.assertEqual(experiment, impression_event.experiment) self.assertEqual(variation, impression_event.variation) self.assertEqual(user_id, impression_event.user_id) + self.assertEqual(self.project_config.region, impression_event.event_context.region) self.assertEqual( [x.__dict__ for x in expected_attrs], [x.__dict__ for x in impression_event.visitor_attributes], ) @@ -91,6 +107,35 @@ def test_conversion_event(self): self.assertEqual(self.project_config.bot_filtering, conversion_event.bot_filtering) self.assertEqual(self.project_config.get_event(event_key), conversion_event.event) self.assertEqual(user_id, conversion_event.user_id) + self.assertEqual(self.project_config.region, conversion_event.event_context.region) + self.assertEqual( + [x.__dict__ for x in expected_attrs], [x.__dict__ for x in conversion_event.visitor_attributes], + ) + + def test_conversion_event_eu(self): + project_config = self.project_config + user_id = 'test_user' + event_key = 'test_event' + user_attributes = {'test_attribute': 'test_value', 'boolean_key': True} + + project_config.region = 'EU' + + conversion_event = UserEventFactory.create_conversion_event( + project_config, event_key, user_id, user_attributes, None + ) + + expected_attrs = EventFactory.build_attribute_list(user_attributes, project_config) + + self.assertEqual(self.project_config.project_id, conversion_event.event_context.project_id) + self.assertEqual(self.project_config.revision, conversion_event.event_context.revision) + self.assertEqual(self.project_config.account_id, conversion_event.event_context.account_id) + self.assertEqual( + self.project_config.anonymize_ip, conversion_event.event_context.anonymize_ip, + ) + self.assertEqual(self.project_config.bot_filtering, conversion_event.bot_filtering) + self.assertEqual(self.project_config.get_event(event_key), conversion_event.event) + self.assertEqual(user_id, conversion_event.user_id) + self.assertEqual('EU', conversion_event.event_context.region) self.assertEqual( [x.__dict__ for x in expected_attrs], [x.__dict__ for x in conversion_event.visitor_attributes], ) @@ -117,6 +162,7 @@ def test_conversion_event__with_event_tags(self): self.assertEqual(self.project_config.bot_filtering, conversion_event.bot_filtering) self.assertEqual(self.project_config.get_event(event_key), conversion_event.event) self.assertEqual(user_id, conversion_event.user_id) + self.assertEqual(self.project_config.region, conversion_event.event_context.region) self.assertEqual( [x.__dict__ for x in expected_attrs], [x.__dict__ for x in conversion_event.visitor_attributes], )