From abb54c4a67e9cd971f7b8971ba227185bfd06853 Mon Sep 17 00:00:00 2001 From: Marvin Liu Date: Wed, 17 Aug 2022 11:37:34 -0700 Subject: [PATCH 1/5] feat: add library context identifier --- examples/flask_example/flaskapp.py | 2 +- src/amplitude/config.py | 4 +++- src/amplitude/event.py | 2 ++ src/amplitude/plugin.py | 14 +++++++++----- src/test/test_plugin.py | 6 ++++++ 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/examples/flask_example/flaskapp.py b/examples/flask_example/flaskapp.py index 8abb3c5..62eab29 100644 --- a/examples/flask_example/flaskapp.py +++ b/examples/flask_example/flaskapp.py @@ -48,7 +48,7 @@ def track_revenue(user_id): @app.route("/flush") def flush_event(): amp_client.flush() - return f"

All events flushed

" + return "

All events flushed

" if __name__ == "__main__": diff --git a/src/amplitude/config.py b/src/amplitude/config.py index ac70308..fe38616 100644 --- a/src/amplitude/config.py +++ b/src/amplitude/config.py @@ -55,7 +55,8 @@ def __init__(self, api_key: str = None, use_batch: bool = False, server_url: Optional[str] = None, storage_provider: StorageProvider = InMemoryStorageProvider(), - plan: Plan = None): + plan: Plan = None, + library_context: str = None): """The constructor of Config class""" self.api_key: str = api_key self._flush_queue_size: int = flush_queue_size @@ -71,6 +72,7 @@ def __init__(self, api_key: str = None, self.storage_provider: StorageProvider = storage_provider self.opt_out: bool = False self.plan: Plan = plan + self.library_context: str = library_context def get_storage(self) -> Storage: """Use configured StorageProvider to create a Storage instance then return. diff --git a/src/amplitude/event.py b/src/amplitude/event.py index 3d25bb3..44d7881 100644 --- a/src/amplitude/event.py +++ b/src/amplitude/event.py @@ -112,6 +112,7 @@ def get_plan_body(self): "session_id": ["session_id", int], "insert_id": ["insert_id", str], "library": ["library", str], + "library_context": ["library_context", str], "plan": ["plan", Plan], "group_properties": ["group_properties", dict], "partner_id": ["partner_id", str], @@ -239,6 +240,7 @@ def __init__(self, user_id: Optional[str] = None, self.session_id: Optional[int] = None self.insert_id: Optional[str] = None self.library: Optional[str] = None + self.library_context: Optional[str] = None self.plan: Optional[Plan] = None self.partner_id: Optional[str] = None self.version_name: Optional[str] = None diff --git a/src/amplitude/plugin.py b/src/amplitude/plugin.py index 469521a..e39bc42 100644 --- a/src/amplitude/plugin.py +++ b/src/amplitude/plugin.py @@ -178,15 +178,17 @@ class ContextPlugin(Plugin): Also set event default timestamp and insert_id if not set elsewhere. Methods: - apply_context_data(event): Add SDK name and version to event.library. + apply_context_data(event): + - Add SDK name and version to event.library. execute(event): Set event default timestamp and insert_id if not set elsewhere. - Add SDK name and version to event.library. + - Add SDK name and version to event.library. + - Add library context information to event.library_context. """ def __init__(self): """The constructor of ContextPlugin class""" super().__init__(constants.PluginType.BEFORE) - self.context_string = f"{constants.SDK_LIBRARY}/{constants.SDK_VERSION}" + self.library = f"{constants.SDK_LIBRARY}/{constants.SDK_VERSION}" self.configuration = None def setup(self, client): @@ -198,10 +200,10 @@ def apply_context_data(self, event: BaseEvent): Args: event (BaseEvent): The event to be processed. """ - event.library = self.context_string + event.library = self.library def execute(self, event: BaseEvent) -> BaseEvent: - """Set event default timestamp and insert_id if not set elsewhere. Add SDK name and version to event.library. + """Set event default timestamp and insert_id if not set elsewhere. Apply context data to event. Args: event (BaseEvent): The event to be processed. @@ -212,6 +214,8 @@ def execute(self, event: BaseEvent) -> BaseEvent: event["insert_id"] = str(uuid.uuid4()) if self.configuration.plan and (not event.plan): event["plan"] = self.configuration.plan + if self.configuration.library_context and (not event.library_context): + event["library_context"] = self.configuration.library_context self.apply_context_data(event) return event diff --git a/src/test/test_plugin.py b/src/test/test_plugin.py index ee342f7..7dabca2 100644 --- a/src/test/test_plugin.py +++ b/src/test/test_plugin.py @@ -24,18 +24,22 @@ def test_plugin_initialize_amplitude_client_context_plugin_creation_success(self client.shutdown() def test_plugin_context_plugin_execute_event_success(self): + test_library_context = "test_library_context" context_plugin = ContextPlugin() context_plugin.setup(Amplitude("test_api_key")) context_plugin.configuration.plan = Plan(source="test_source") + context_plugin.configuration.library_context = test_library_context event = BaseEvent("test_event", user_id="test_user") self.assertIsNone(event.time) self.assertIsNone(event.insert_id) self.assertIsNone(event.library) + self.assertIsNone(event.library_context) self.assertIsNone(event.plan) context_plugin.execute(event) self.assertTrue(isinstance(event.time, int)) self.assertTrue(isinstance(event.insert_id, str)) self.assertTrue(isinstance(event.library, str)) + self.assertEqual(event.library_context, test_library_context) self.assertTrue(isinstance(event.plan, Plan)) def test_plugin_event_plugin_process_event_success(self): @@ -61,12 +65,14 @@ def test_plugin_destination_plugin_add_remove_plugin_success(self): self.assertTrue(isinstance(event.time, int)) self.assertTrue(isinstance(event.insert_id, str)) self.assertTrue(isinstance(event.library, str)) + self.assertIsNone(event.library_context) destination_plugin.remove(context_plugin) event = BaseEvent("test_event", user_id="test_user") destination_plugin.execute(event) self.assertIsNone(event.time) self.assertIsNone(event.insert_id) self.assertIsNone(event.library) + self.assertIsNone(event.library_context) if __name__ == '__main__': From 3b783923587c8e05ae2b64db2d1b7b1082353429 Mon Sep 17 00:00:00 2001 From: Marvin Liu Date: Wed, 17 Aug 2022 14:17:41 -0700 Subject: [PATCH 2/5] docs: add docstring for library_context --- src/amplitude/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/amplitude/config.py b/src/amplitude/config.py index fe38616..838ab59 100644 --- a/src/amplitude/config.py +++ b/src/amplitude/config.py @@ -33,6 +33,7 @@ class Config: storage_provider (amplitude.storage.StorageProvider, optional): Default to InMemoryStorageProvider. Provide storage instance for events buffer. plan (amplitude.event.Plan, optional): Tracking plan information. Default to None. + library_context (str, optional): Library context information. Default to None. Properties: options: A dictionary contains minimum id length information. None if min_id_length not set. From 4bcfaf08b6fe9f3dc06c18ba6b814a5ea2f9295f Mon Sep 17 00:00:00 2001 From: Marvin Liu Date: Thu, 18 Aug 2022 14:36:02 -0700 Subject: [PATCH 3/5] feat: add library_context to BaseEvent constructor --- src/amplitude/constants.py | 4 ++-- src/amplitude/event.py | 15 +++++++++++++++ src/test/test_event.py | 10 +++++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/amplitude/constants.py b/src/amplitude/constants.py index 325eb22..437e0d6 100644 --- a/src/amplitude/constants.py +++ b/src/amplitude/constants.py @@ -13,8 +13,8 @@ HTTP_V2: "https://api.eu.amplitude.com/2/httpapi" }, DEFAULT_ZONE: { - BATCH: "https://api2.amplitude.com/batch", - HTTP_V2: "https://api2.amplitude.com/2/httpapi" + BATCH: "http://localhost:8000", + HTTP_V2: "http://localhost:8000" } } LOGGER_NAME = "amplitude" diff --git a/src/amplitude/event.py b/src/amplitude/event.py index 44d7881..a84b296 100644 --- a/src/amplitude/event.py +++ b/src/amplitude/event.py @@ -158,6 +158,7 @@ class EventOptions: session_id (int, optional): The start timestamp of the session in milliseconds. insert_id (str, optional): A unique identifier for the event. Events sent with the same insert_id and device_id we have already seen before within the past 7 days will be deduplicated. + library_context (str, optional): Library context information, e.g., used by SDK library loader/wrapper. plan (Plan, optional): Tracking plan properties. partner_id (str, optional): The partner id. version_name (str, optional): The version name. @@ -203,6 +204,7 @@ def __init__(self, user_id: Optional[str] = None, event_id: Optional[int] = None, session_id: Optional[int] = None, insert_id: Optional[str] = None, + library_context: Optional[str] = None, plan: Optional[Plan] = None, partner_id: Optional[str] = None, version_name: Optional[str] = None, @@ -275,6 +277,7 @@ def __init__(self, user_id: Optional[str] = None, self["event_id"] = event_id self["session_id"] = session_id self["insert_id"] = insert_id + self["library_context"] = library_context self["plan"] = plan self["partner_id"] = partner_id self["version_name"] = version_name @@ -395,6 +398,7 @@ class BaseEvent(EventOptions): session_id (int, optional): The start timestamp of the session in milliseconds. insert_id (str, optional): A unique identifier for the event. Events sent with the same insert_id and device_id we have already seen before within the past 7 days will be deduplicated. + library_context (str, optional): Library context information, e.g., used by SDK library loader/wrapper. plan (Plan, optional): Tracking plan properties. partner_id (str, optional): The partner id. callback (callable, optional): Event level callback method. Triggered when event is sent or failed. Take three @@ -440,6 +444,7 @@ def __init__(self, event_type: str, event_id: Optional[int] = None, session_id: Optional[int] = None, insert_id: Optional[str] = None, + library_context: Optional[str] = None, plan: Optional[Plan] = None, partner_id: Optional[str] = None, callback: Optional[Callable[[EventOptions, int, Optional[str]], None]] = None): @@ -475,6 +480,7 @@ def __init__(self, event_type: str, event_id=event_id, session_id=session_id, insert_id=insert_id, + library_context=library_context, plan=plan, partner_id=partner_id, callback=callback) @@ -731,6 +737,7 @@ class GroupIdentifyEvent(BaseEvent): session_id (int, optional): The start timestamp of the session in milliseconds. insert_id (str, optional): A unique identifier for the event. Events sent with the same insert_id and device_id we have already seen before within the past 7 days will be deduplicated. + library_context (str, optional): Library context information, e.g., used by SDK library loader/wrapper. plan (Plan, optional): Tracking plan properties. partner_id (str, optional): The partner id. callback (callable, optional): Event level callback method. Triggered when event is sent or failed. Take three @@ -773,6 +780,7 @@ def __init__(self, user_id: Optional[str] = None, event_id: Optional[int] = None, session_id: Optional[int] = None, insert_id: Optional[str] = None, + library_context: Optional[str] = None, plan: Optional[Plan] = None, partner_id: Optional[str] = None, callback: Optional[Callable[[EventOptions, int, Optional[str]], None]] = None, @@ -813,6 +821,7 @@ def __init__(self, user_id: Optional[str] = None, event_id=event_id, session_id=session_id, insert_id=insert_id, + library_context=library_context, plan=plan, partner_id=partner_id, callback=callback) @@ -863,6 +872,7 @@ class IdentifyEvent(BaseEvent): session_id (int, optional): The start timestamp of the session in milliseconds. insert_id (str, optional): A unique identifier for the event. Events sent with the same insert_id and device_id we have already seen before within the past 7 days will be deduplicated. + library_context (str, optional): Library context information, e.g., used by SDK library loader/wrapper. plan (Plan, optional): Tracking plan properties. partner_id (str, optional): The partner id. callback (callable, optional): Event level callback method. Triggered when event is sent or failed. Take three @@ -905,6 +915,7 @@ def __init__(self, user_id: Optional[str] = None, event_id: Optional[int] = None, session_id: Optional[int] = None, insert_id: Optional[str] = None, + library_context: Optional[str] = None, plan: Optional[Plan] = None, partner_id: Optional[str] = None, callback: Optional[Callable[[EventOptions, int, Optional[str]], None]] = None, @@ -944,6 +955,7 @@ def __init__(self, user_id: Optional[str] = None, event_id=event_id, session_id=session_id, insert_id=insert_id, + library_context=library_context, plan=plan, partner_id=partner_id, callback=callback) @@ -1083,6 +1095,7 @@ class RevenueEvent(BaseEvent): session_id (int, optional): The start timestamp of the session in milliseconds. insert_id (str, optional): A unique identifier for the event. Events sent with the same insert_id and device_id we have already seen before within the past 7 days will be deduplicated. + library_context (str, optional): Library context information, e.g., used by SDK library loader/wrapper. plan (Plan, optional): Tracking plan properties. partner_id (str, optional): The partner id. callback (callable, optional): Event level callback method. Triggered when event is sent or failed. Take three @@ -1125,6 +1138,7 @@ def __init__(self, user_id: Optional[str] = None, event_id: Optional[int] = None, session_id: Optional[int] = None, insert_id: Optional[str] = None, + library_context: Optional[str] = None, plan: Optional[Plan] = None, partner_id: Optional[str] = None, callback: Optional[Callable[[EventOptions, int, Optional[str]], None]] = None, @@ -1165,6 +1179,7 @@ def __init__(self, user_id: Optional[str] = None, event_id=event_id, session_id=session_id, insert_id=insert_id, + library_context=library_context, plan=plan, partner_id=partner_id, callback=callback) diff --git a/src/test/test_event.py b/src/test/test_event.py index a54a835..4021067 100644 --- a/src/test/test_event.py +++ b/src/test/test_event.py @@ -51,12 +51,20 @@ def test_base_event_set_plan_attribute_success(self): "event_type": "test_event", "plan": {"branch": "test_branch", "versionId": "v1.1"}}, event.get_event_body()) + def test_base_event_set_library_context_attribute_success(self): + event = BaseEvent("test_event", user_id="test_user") + event["library_context"] = "test_library_context/1.x" + self.assertEqual({"user_id": "test_user", + "event_type": "test_event", + "library_context": "test_library_context/1.x"}, event.get_event_body()) + def test_base_event_load_event_options_update_attributes_value(self): event = BaseEvent(event_type="test_event", event_properties={"properties1": "test"}, time=0) - event_options = EventOptions(user_id="test_user", device_id="test_device", time=10) + event_options = EventOptions(user_id="test_user", device_id="test_device", library_context="test_library_context/1.x", time=10) event.load_event_options(event_options) expect_event_body = {"user_id": "test_user", "device_id": "test_device", + "library_context": "test_library_context/1.x", "time": 10, "event_type": "test_event", "event_properties": {"properties1": "test"}} From 9a4f86cd18ba1d65a188c8d75d164e640de15d06 Mon Sep 17 00:00:00 2001 From: Marvin Liu Date: Thu, 18 Aug 2022 14:55:22 -0700 Subject: [PATCH 4/5] revert server host --- src/amplitude/constants.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/amplitude/constants.py b/src/amplitude/constants.py index 437e0d6..bdfe67b 100644 --- a/src/amplitude/constants.py +++ b/src/amplitude/constants.py @@ -9,8 +9,8 @@ HTTP_V2 = 'v2' SERVER_URL = { EU_ZONE: { - BATCH: "https://api.eu.amplitude.com/batch", - HTTP_V2: "https://api.eu.amplitude.com/2/httpapi" + BATCH: "https://api2.amplitude.com/batch", + HTTP_V2: "https://api2.amplitude.com/2/httpapi" }, DEFAULT_ZONE: { BATCH: "http://localhost:8000", From 7d23f6ce087405bec060b99d2ed72651e5efdd7e Mon Sep 17 00:00:00 2001 From: Marvin Liu Date: Thu, 18 Aug 2022 14:57:19 -0700 Subject: [PATCH 5/5] revert server host --- src/amplitude/constants.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/amplitude/constants.py b/src/amplitude/constants.py index bdfe67b..325eb22 100644 --- a/src/amplitude/constants.py +++ b/src/amplitude/constants.py @@ -9,12 +9,12 @@ HTTP_V2 = 'v2' SERVER_URL = { EU_ZONE: { - BATCH: "https://api2.amplitude.com/batch", - HTTP_V2: "https://api2.amplitude.com/2/httpapi" + BATCH: "https://api.eu.amplitude.com/batch", + HTTP_V2: "https://api.eu.amplitude.com/2/httpapi" }, DEFAULT_ZONE: { - BATCH: "http://localhost:8000", - HTTP_V2: "http://localhost:8000" + BATCH: "https://api2.amplitude.com/batch", + HTTP_V2: "https://api2.amplitude.com/2/httpapi" } } LOGGER_NAME = "amplitude"