diff --git a/optimizely/event/event_factory.py b/optimizely/event/event_factory.py index 8a4bb0cf..66f50442 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' @@ -83,6 +86,7 @@ def create_log_event( return None user_context = first_event.event_context + region_value = user_context.region.value if hasattr(user_context.region, 'value') else user_context.region event_batch = payload.EventBatch( user_context.account_id, user_context.project_id, @@ -90,6 +94,7 @@ def create_log_event( user_context.client_name, user_context.client_version, user_context.anonymize_ip, + region_value, True, ) @@ -97,7 +102,10 @@ 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_str = user_context.region.value if hasattr(user_context.region, 'value') else str(user_context.region) + endpoint = cls.EVENT_ENDPOINTS.get(region_str, 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/payload.py b/optimizely/event/payload.py index ac6f35e4..36917230 100644 --- a/optimizely/event/payload.py +++ b/optimizely/event/payload.py @@ -34,6 +34,7 @@ def __init__( anonymize_ip: bool, enrich_decisions: bool = True, visitors: Optional[list[Visitor]] = None, + region: str = 'US' ): self.account_id = account_id self.project_id = project_id @@ -43,6 +44,7 @@ def __init__( self.anonymize_ip = anonymize_ip self.enrich_decisions = enrich_decisions self.visitors = visitors or [] + self.region = region def __eq__(self, other: object) -> bool: batch_obj = self.get_event_params() diff --git a/optimizely/event/user_event.py b/optimizely/event/user_event.py index 9cdb623a..243234d3 100644 --- a/optimizely/event/user_event.py +++ b/optimizely/event/user_event.py @@ -17,6 +17,7 @@ from sys import version_info from optimizely import version +from optimizely.project_config import Region if version_info < (3, 8): @@ -97,10 +98,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: Region): 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 ef07d06b..a15d8f57 100644 --- a/optimizely/event/user_event_factory.py +++ b/optimizely/event/user_event_factory.py @@ -76,7 +76,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( @@ -115,7 +119,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..eb80bdd0 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'} @@ -246,7 +249,8 @@ def _get_required_params_for_conversion( def create_impression_event( self, project_config: ProjectConfig, experiment: Experiment, - variation_id: str, user_id: str, attributes: UserAttributes + variation_id: str, user_id: str, attributes: UserAttributes, + region: str = 'US' ) -> Event: """ Create impression Event to be sent to the logging endpoint. @@ -266,11 +270,17 @@ 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) + params['region'] = project_config.region.value + + region = project_config.region or 'US' + events_url = self.EVENTS_URLS.get(str(region), 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, - user_id: str, attributes: UserAttributes, event_tags: event_tag_utils.EventTags + user_id: str, attributes: UserAttributes, event_tags: event_tag_utils.EventTags, + region: str = 'US' ) -> Event: """ Create conversion Event to be sent to the logging endpoint. @@ -289,4 +299,10 @@ 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) + + params['region'] = project_config.region.value + + region = project_config.region or 'US' + events_url = self.EVENTS_URLS.get(str(region), 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..bfbf7ca5 100644 --- a/optimizely/project_config.py +++ b/optimizely/project_config.py @@ -14,6 +14,7 @@ import json from typing import TYPE_CHECKING, Optional, Type, TypeVar, cast, Any, Iterable, List from sys import version_info +from enum import Enum from . import entities from . import exceptions @@ -42,6 +43,11 @@ EntityClass = TypeVar('EntityClass') +class Region(str, Enum): + US = 'US' + EU = 'EU' + + class ProjectConfig: """ Representation of the Optimizely project config. """ @@ -85,6 +91,13 @@ 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: Region + if region_value == Region.EU.value: + self.region = Region.EU + else: + self.region = Region.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/base.py b/tests/base.py index 875a26e6..63344d4a 100644 --- a/tests/base.py +++ b/tests/base.py @@ -57,6 +57,7 @@ def fake_server_response(self, status_code: Optional[int] = None, def setUp(self, config_dict='config_dict'): self.config_dict = { + 'region': 'US', 'revision': '42', 'sdkKey': 'basic-test', 'version': '2', @@ -150,6 +151,7 @@ def setUp(self, config_dict='config_dict'): # datafile version 4 self.config_dict_with_features = { + 'region': 'US', 'revision': '1', 'sdkKey': 'features-test', 'accountId': '12001', @@ -553,6 +555,7 @@ def setUp(self, config_dict='config_dict'): } self.config_dict_with_multiple_experiments = { + 'region': 'US', 'revision': '42', 'sdkKey': 'multiple-experiments', 'version': '2', @@ -686,6 +689,7 @@ def setUp(self, config_dict='config_dict'): 'accountId': '10367498574', 'events': [{'experimentIds': ['10420810910'], 'id': '10404198134', 'key': 'winning'}], 'revision': '1337', + 'region': 'US', } self.config_dict_with_typed_audiences = { @@ -1078,6 +1082,7 @@ def setUp(self, config_dict='config_dict'): ], 'revision': '3', 'sdkKey': 'typed-audiences', + 'region': 'US', } self.config_dict_with_audience_segments = { @@ -1274,7 +1279,8 @@ def setUp(self, config_dict='config_dict'): } ], 'revision': '101', - 'sdkKey': 'segments-test' + 'sdkKey': 'segments-test', + 'region': 'US', } config = getattr(self, config_dict) diff --git a/tests/test_config.py b/tests/test_config.py index 9ec5c761..2d7fff5f 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -21,7 +21,7 @@ from optimizely import logger from optimizely import optimizely from optimizely.helpers import enums -from optimizely.project_config import ProjectConfig +from optimizely.project_config import ProjectConfig, Region from . import base @@ -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, 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, 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, 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..593d084c 100644 --- a/tests/test_event_builder.py +++ b/tests/test_event_builder.py @@ -86,6 +86,7 @@ def test_create_impression_event(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -100,7 +101,7 @@ 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, @@ -141,6 +142,7 @@ def test_create_impression_event__with_attributes(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -155,7 +157,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, @@ -194,6 +196,7 @@ def test_create_impression_event_when_attribute_is_not_in_datafile(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -205,10 +208,11 @@ def test_create_impression_event_when_attribute_is_not_in_datafile(self): '111129', 'test_user', {'do_you_know_me': 'test_value'}, + 'US' ) 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, @@ -250,6 +254,7 @@ def test_create_impression_event_calls_is_attribute_valid(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } def side_effect(*args, **kwargs): @@ -276,11 +281,12 @@ def side_effect(*args, **kwargs): '111129', 'test_user', attributes, + 'US', ) 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, @@ -328,6 +334,7 @@ def test_create_impression_event__with_user_agent_when_bot_filtering_is_enabled( 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -341,11 +348,12 @@ def test_create_impression_event__with_user_agent_when_bot_filtering_is_enabled( '111129', 'test_user', {'$opt_user_agent': 'Edge'}, + 'US' ) 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, @@ -392,6 +400,7 @@ def test_create_impression_event__with_empty_attributes_when_bot_filtering_is_en 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -405,11 +414,12 @@ def test_create_impression_event__with_empty_attributes_when_bot_filtering_is_en '111129', 'test_user', None, + 'US' ) 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, @@ -462,6 +472,7 @@ def test_create_impression_event__with_user_agent_when_bot_filtering_is_disabled 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -475,11 +486,12 @@ def test_create_impression_event__with_user_agent_when_bot_filtering_is_disabled '111129', 'test_user', {'$opt_user_agent': 'Chrome'}, + 'US' ) 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, @@ -515,17 +527,18 @@ def test_create_conversion_event(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with 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.project_config, 'test_event', 'test_user', None, None, 'US' ) 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, @@ -563,17 +576,18 @@ def test_create_conversion_event__with_attributes(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with 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', {'test_attribute': 'test_value'}, None, + self.project_config, 'test_event', 'test_user', {'test_attribute': 'test_value'}, None, 'US' ) 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, @@ -618,6 +632,7 @@ def test_create_conversion_event__with_user_agent_when_bot_filtering_is_enabled( 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -626,12 +641,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, 'US' ) 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, @@ -681,6 +696,7 @@ def test_create_conversion_event__with_user_agent_when_bot_filtering_is_disabled 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -689,12 +705,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, 'US' ) 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, @@ -735,6 +751,7 @@ def test_create_conversion_event__with_event_tags(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -746,10 +763,11 @@ def test_create_conversion_event__with_event_tags(self): 'test_user', {'test_attribute': 'test_value'}, {'revenue': 4200, 'value': 1.234, 'non-revenue': 'abc'}, + 'US' ) 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, @@ -788,6 +806,7 @@ def test_create_conversion_event__with_invalid_event_tags(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -799,10 +818,11 @@ def test_create_conversion_event__with_invalid_event_tags(self): 'test_user', {'test_attribute': 'test_value'}, {'revenue': '4200', 'value': True, 'non-revenue': 'abc'}, + 'US' ) 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, @@ -843,6 +863,7 @@ def test_create_conversion_event__when_event_is_used_in_multiple_experiments(sel 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -854,10 +875,11 @@ def test_create_conversion_event__when_event_is_used_in_multiple_experiments(sel 'test_user', {'test_attribute': 'test_value'}, {'revenue': 4200, 'value': 1.234, 'non-revenue': 'abc'}, + 'US' ) 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 adbebd35..fd73582a 100644 --- a/tests/test_event_factory.py +++ b/tests/test_event_factory.py @@ -98,6 +98,7 @@ def test_create_impression_event(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -118,7 +119,11 @@ 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_attributes(self): @@ -162,6 +167,7 @@ def test_create_impression_event__with_attributes(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -182,7 +188,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): @@ -224,6 +234,7 @@ def test_create_impression_event_when_attribute_is_not_in_datafile(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -244,7 +255,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): @@ -287,6 +302,7 @@ def test_create_impression_event_calls_is_attribute_valid(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } def side_effect(*args, **kwargs): @@ -296,39 +312,39 @@ def side_effect(*args, **kwargs): return False - attributes = { - 'test_attribute': 'test_value', - 'boolean_key': True, - 'integer_key': 0, - 'double_key': 5.5, - } - - with mock.patch('time.time', return_value=42.123), mock.patch( - 'uuid.uuid4', return_value='a68cf1ad-0393-4e18-af87-efe8f01a7c9c' - ), mock.patch( - 'optimizely.helpers.validator.is_attribute_valid', side_effect=side_effect, - ): - - event_obj = UserEventFactory.create_impression_event( - self.project_config, - self.project_config.get_experiment_from_key('test_experiment'), - '111129', - '', - 'experiment', - 'test_user', - attributes, - ) - - 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, + attributes = { + 'test_attribute': 'test_value', + 'boolean_key': True, + 'integer_key': 0, + 'double_key': 5.5, + } + + with mock.patch('time.time', return_value=42.123), mock.patch( + 'uuid.uuid4', return_value='a68cf1ad-0393-4e18-af87-efe8f01a7c9c' + ), mock.patch( + 'optimizely.helpers.validator.is_attribute_valid', side_effect=side_effect, + ): + + event_obj = UserEventFactory.create_impression_event( + self.project_config, + self.project_config.get_experiment_from_key('test_experiment'), + '111129', + '', + 'experiment', + 'test_user', + attributes, ) + log_event = EventFactory.create_log_event(event_obj, self.logger) + + self._validate_event_object( + 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_enabled(self,): """ Test that create_impression_event creates Event object with right params when user agent attribute is provided and @@ -377,6 +393,7 @@ def test_create_impression_event__with_user_agent_when_bot_filtering_is_enabled( 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -399,7 +416,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,): @@ -449,6 +470,7 @@ def test_create_impression_event__with_empty_attributes_when_bot_filtering_is_en 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -471,7 +493,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,): @@ -527,6 +553,7 @@ def test_create_impression_event__with_user_agent_when_bot_filtering_is_disabled 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -549,7 +576,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): @@ -582,6 +613,7 @@ def test_create_conversion_event(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -594,7 +626,11 @@ 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_attributes(self): @@ -629,6 +665,7 @@ def test_create_conversion_event__with_attributes(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -641,7 +678,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,): @@ -683,6 +724,7 @@ def test_create_conversion_event__with_user_agent_when_bot_filtering_is_enabled( 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -697,7 +739,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,): @@ -744,6 +790,7 @@ def test_create_conversion_event__with_user_agent_when_bot_filtering_is_disabled 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -758,7 +805,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): @@ -796,6 +847,7 @@ def test_create_conversion_event__with_event_tags(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -812,7 +864,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): @@ -848,6 +904,7 @@ def test_create_conversion_event__with_invalid_event_tags(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -864,7 +921,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): @@ -902,6 +963,7 @@ def test_create_conversion_event__when_event_is_used_in_multiple_experiments(sel 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } with mock.patch('time.time', return_value=42.123), mock.patch( @@ -918,5 +980,9 @@ 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, ) diff --git a/tests/test_optimizely.py b/tests/test_optimizely.py index 1f4293cd..2c9caa87 100644 --- a/tests/test_optimizely.py +++ b/tests/test_optimizely.py @@ -365,6 +365,7 @@ def test_activate(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -385,6 +386,76 @@ def test_activate(self): {'Content-Type': 'application/json'}, ) + def test_activate_with_eu_hosting(self): + """ Test that activate calls process with right params and returns expected variation. """ + """ Test EU hosting for activate method. """ + + with mock.patch( + 'optimizely.decision_service.DecisionService.get_variation', + return_value=(self.project_config.get_variation_from_id('test_experiment', '111129'), []), + ) as mock_decision, mock.patch('time.time', return_value=42), mock.patch( + 'uuid.uuid4', return_value='a68cf1ad-0393-4e18-af87-efe8f01a7c9c' + ), mock.patch( + 'optimizely.event.event_processor.BatchEventProcessor.process' + ) as mock_process: + self.assertEqual('variation', self.optimizely.activate('test_experiment', 'test_user')) + + 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': 'test_experiment', + 'rule_type': 'experiment', + 'variation_key': 'variation', + 'enabled': True}, + } + ], + 'events': [ + { + 'timestamp': 42000, + 'entity_id': '111182', + 'uuid': 'a68cf1ad-0393-4e18-af87-efe8f01a7c9c', + 'key': 'campaign_activated', + } + ], + } + ], + } + ], + 'client_version': version.__version__, + 'client_name': 'python-sdk', + 'enrich_decisions': True, + 'anonymize_ip': False, + 'revision': '42', + 'region': 'EU', + } + + log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) + user_context = mock_decision.call_args[0][2] + user_profile_tracker = mock_decision.call_args[0][3] + + mock_decision.assert_called_once_with( + self.project_config, self.project_config.get_experiment_from_key('test_experiment'), + user_context, user_profile_tracker + ) + self.assertEqual(1, mock_process.call_count) + + self._validate_event_object( + log_event.__dict__, + 'https://eu.logx.optimizely.com/v1/events', + expected_params, + 'POST', + {'Content-Type': 'application/json'}, + ) + def test_add_activate_remove_clear_listener(self): callbackhit = [False] """ Test adding a listener activate passes correctly and gets called""" @@ -764,6 +835,7 @@ def test_activate__with_attributes__audience_match(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -848,6 +920,7 @@ def test_activate__with_attributes_of_different_types(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -1044,6 +1117,7 @@ def test_activate__with_attributes__audience_match__forced_bucketing(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -1120,6 +1194,7 @@ def test_activate__with_attributes__audience_match__bucketing_id_provided(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -1288,6 +1363,7 @@ def test_track__with_attributes(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -1424,6 +1500,7 @@ def test_track__with_attributes__bucketing_id_provided(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -1504,6 +1581,7 @@ def test_track__with_event_tags(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -1560,6 +1638,7 @@ def test_track__with_event_tags_revenue(self): 'account_id': '12001', 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -1648,6 +1727,7 @@ def test_track__with_event_tags__forced_bucketing(self): 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -1703,6 +1783,7 @@ def test_track__with_invalid_event_tags(self): 'account_id': '12001', 'anonymize_ip': False, 'revision': '42', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -2103,6 +2184,7 @@ def test_is_feature_enabled__returns_true_for_feature_experiment_if_feature_enab 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '1', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -2204,6 +2286,7 @@ def test_is_feature_enabled__returns_false_for_feature_experiment_if_feature_dis 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '1', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) @@ -2356,6 +2439,7 @@ def test_is_feature_enabled__returns_true_for_feature_rollout_if_feature_enabled 'enrich_decisions': True, 'anonymize_ip': False, 'revision': '1', + 'region': 'US', } log_event = EventFactory.create_log_event(mock_process.call_args[0][0], self.optimizely.logger) diff --git a/tests/test_user_event_factory.py b/tests/test_user_event_factory.py index 009ef05d..4e4d17d1 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 + ) + + 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,7 @@ 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], ) @@ -117,6 +134,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], ) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy