From 870b89c14e35c7b1844a89c1853e21081a9f8cf6 Mon Sep 17 00:00:00 2001 From: neelkanth-kaushik Date: Tue, 25 Mar 2025 00:26:16 +0530 Subject: [PATCH 1/3] This commit contains code for Analytics Python CLI E2E Testing Tool - LIBRARIES-2434 --- .gitignore | 1 + e2e-test/.env | 6 +++ e2e-test/README.md | 88 ++++++++++++++++++++++++++++++++++++ e2e-test/__init__.py | 0 e2e-test/main.py | 1 + e2e-test/requirements.txt | 6 +++ e2e-test/setup.py | 15 +++++++ e2e-test/src/__init__.py | 0 e2e-test/src/cli.py | 93 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 210 insertions(+) create mode 100644 e2e-test/.env create mode 100644 e2e-test/README.md create mode 100644 e2e-test/__init__.py create mode 100644 e2e-test/main.py create mode 100644 e2e-test/requirements.txt create mode 100644 e2e-test/setup.py create mode 100644 e2e-test/src/__init__.py create mode 100644 e2e-test/src/cli.py diff --git a/.gitignore b/.gitignore index b856542a..767be8d3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ build .vscode/ .idea/ .python-version +.venv \ No newline at end of file diff --git a/e2e-test/.env b/e2e-test/.env new file mode 100644 index 00000000..daa06fa2 --- /dev/null +++ b/e2e-test/.env @@ -0,0 +1,6 @@ +APP_NAME='e2e-test' +DEBUG_MODE = False +SEND_EVENTS = True +LOG_DIRECTORY='logs' +LOG_FILENAME_SUFFIX='e2e-test' +LOG_FORMAT='%(asctime)s - %(name)s - %(levelname)s - %(message)s' \ No newline at end of file diff --git a/e2e-test/README.md b/e2e-test/README.md new file mode 100644 index 00000000..8539a5f9 --- /dev/null +++ b/e2e-test/README.md @@ -0,0 +1,88 @@ + +# Analytics Python CLI + +This tool is created for the purpose of E2E Testing. + +## Dependencies + +| Module | Version | +|--|--| +| python | 3.9 | +| click | 8.1.8 | +| python-dotenv | 1.0.1 | +| python-dateutil | 2.8.2 | +| requests | 2.32.3 | +| PyJWT | 2.10.1 | +| backoff | 2.2.1 | + +## Installation + + 1. Change the working directory + ```bash + $ cd e2e-test + ``` + 2. Create a virtual environment inside the working directory + ```bash + $ python3 -m venv .venv + ``` + 3. Enable the virtual environment + ```bash + $ source .venv/bin/activate + ``` + 4. Install dependencies + ```bash + $ pip install -r requirements.txt + ``` + 5. Install the script as a module + ```bash + $ pip install --editable . + ``` + +## Usage Examples with Sample Payloads + +### 1. Identify + + ``` bash +$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c89729de960ea\",\"channel\":\"browser\",\"context\":{\"ip\":\"8.8.8.8\",\"userAgent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36\"},\"integrations\":{\"All\":false,\"Mixpanel\":true,\"Salesforce\":true},\"messageId\":\"022bb90c-bbac-11e4-8dfc-aa07a5b093q8\",\"traits\":{\"name\":\"Clark Kent\",\"email\":\"clark@example.com\",\"plan\":\"premium\",\"logins\":5,\"address\":{\"street\":\"6th St\",\"city\":\"San Francisco\",\"state\":\"CA\",\"postalCode\":\"94103\",\"country\":\"USA\"}},\"type\":\"identify\",\"userId\":\"97980cfea0062\",\"version\":\"2.0\"}"' +``` + +### 2. Track + +``` bash +$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"messageId\":\"122bb90c-bbac-11e4-8dfc-aa07z5b098ip\",\"userId\":\"AiUGstSDIg\",\"type\":\"track\",\"event\":\"Course Clicked\",\"context\":{\"page\":{\"path\":\"/academy/\",\"referrer\":\"\",\"search\":\"\",\"title\":\"Analytics Academy\",\"url\":\"https://segment.com/academy/\"},\"userAgent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\",\"ip\":\"108.0.78.21\"},\"integrations\":{},\"properties\":{\"title\":\"Intro to Analytics\"}}"' +``` + +### 3. Page + +``` bash +$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c19729de860ea\",\"channel\":\"browser\",\"context\":{\"ip\":\"8.8.8.8\",\"userAgent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36\"},\"integrations\":{\"All\":true,\"Mixpanel\":false,\"Salesforce\":false},\"messageId\":\"022bb90c-bbac-11e8-8dfc-aa07a5b090ol\",\"name\":\"Home\",\"properties\":{\"title\":\"Welcome | Initech\",\"url\":\"http://www.example.com\"},\"type\":\"page\",\"userId\":\"97980cfea0067\"}"' +``` + +### 4. Screen + +``` bash +$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c19729de860ea\",\"channel\":\"browser\",\"context\":{\"ip\":\"8.8.8.8\",\"userAgent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36\"},\"integrations\":{\"All\":true,\"Mixpanel\":false,\"Salesforce\":false},\"messageId\":\"022bb90c-bbac-11e4-8dfc-aa07a5b090op\",\"name\":\"Registration\",\"properties\":{\"title\":\"Welcome | Initech\",\"url\":\"http://www.example.com\"},\"type\":\"screen\",\"userId\":\"97980cfea0067\"}"' +``` + +### 5. Alias + +``` bash +$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c19729de800ea\",\"channel\":\"browser\",\"context\":{\"ip\":\"8.8.8.8\",\"userAgent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36\"},\"integrations\":{\"All\":true,\"Mixpanel\":false,\"Salesforce\":false},\"messageId\":\"022bb90c-bbac-11e4-8dfc-aa07u5b093lk\",\"previousId\":\"12345-239239-239239-23923\",\"type\":\"alias\",\"userId\":\"507f191e81\",\"version\":\"1.9\"}"' +``` + +### 6. Group + +``` bash +% e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"type\":\"group\",\"groupId\":\"0e8c78ea9d97a7b8185e8632\",\"userId\":\"EMP12345\",\"traits\":{\"name\":\"Initech\",\"industry\":\"Technology\",\"employees\":329,\"plan\":\"enterprise\",\"total billed\":830}}"' +``` + +## Error Logs + + 1. Errors logs are written under ```logs```directory. + 2. Each day, a new log directory is created under ```logs``` in ```YearMonthDay``` format. + 3. Log files are named as ```e2e-test_.log``` + 4. Log file is rotated is maximum allowed file size is reached. + +## Configuration Options + +A few configuration options are available in the ```.env``` file. \ No newline at end of file diff --git a/e2e-test/__init__.py b/e2e-test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/e2e-test/main.py b/e2e-test/main.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/e2e-test/main.py @@ -0,0 +1 @@ + diff --git a/e2e-test/requirements.txt b/e2e-test/requirements.txt new file mode 100644 index 00000000..bbbf7948 --- /dev/null +++ b/e2e-test/requirements.txt @@ -0,0 +1,6 @@ +click==8.1.8 +python-dotenv==1.0.1 +python-dateutil==2.8.2 +requests==2.32.3 +PyJWT==2.10.1 +backoff==2.2.1 \ No newline at end of file diff --git a/e2e-test/setup.py b/e2e-test/setup.py new file mode 100644 index 00000000..b7333405 --- /dev/null +++ b/e2e-test/setup.py @@ -0,0 +1,15 @@ +from setuptools import setup + +setup( + name='e2e-test', + version='0.1.0', + py_modules=['e2e-test'], + install_requires=[ + 'click', 'python-dotenv', 'python-dateutil', 'requests', 'PyJWT', 'backoff' + ], + entry_points={ + 'console_scripts': [ + 'e2e-test:run = src.cli:run', + ], + }, +) diff --git a/e2e-test/src/__init__.py b/e2e-test/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/e2e-test/src/cli.py b/e2e-test/src/cli.py new file mode 100644 index 00000000..7ee6083c --- /dev/null +++ b/e2e-test/src/cli.py @@ -0,0 +1,93 @@ +import click +from dotenv import load_dotenv +import os +import sys +import logging +from logging.handlers import RotatingFileHandler +import json +from datetime import datetime + +load_dotenv() + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) + +import segment.analytics as analytics # noqa: E402 (ignore autopep8) + + +@click.command() +@click.option('--writeKey', type=str, help='Segment write key') +@click.option('--payload', type=str, help='A JSON string that specifies the event payload.') +def run(writekey, payload): + analytics.write_key = writekey + analytics.debug = os.getenv('DEBUG_MODE') + analytics.send = os.getenv('SEND_EVENTS') + logger = log_config() + + try: + # Decode the JSON payload + decodedJson = json.loads(payload) + data = json.loads(decodedJson) + + specType = data.get('type') if data.get('type') is not None else None + messageId = data.get('messageId') if data.get('messageId') is not None else None + userId = data.get('userId') if data.get('userId') is not None else '' + eventName = data.get('event') if data.get('event') is not None else None + traits = data.get('traits') if data.get('traits') is not None else None + properties = data.get('properties') if data.get('properties') is not None else None + context = data.get('context') if data.get('context') is not None else None + integrations = data.get('integrations') if data.get('integrations') is not None else None + groupId = data.get('groupId') if data.get('groupId') is not None else None + pageOrScreenName = data.get('name') if data.get('name') is not None else None + pageOrScreenCategory = data.get('category') if data.get('category') is not None else None + timestamp = data.get('timestamp') if data.get('timestamp') is not None else None + anonymousId = data.get('anonymousId') if data.get('anonymousId') is not None else '' + previousId = data.get('previousId') if data.get('previousId') is not None else None + if specType == 'identify': + analytics.identify(userId, traits, context, timestamp, anonymousId, integrations, messageId) + elif specType == 'track': + analytics.track(userId, eventName, properties, context, timestamp, anonymousId, integrations, messageId) + elif specType == 'page': + analytics.page(userId, pageOrScreenCategory, pageOrScreenName, properties, + context, timestamp, anonymousId, integrations, messageId) + elif specType == 'screen': + analytics.screen(userId, pageOrScreenCategory, pageOrScreenName, properties, + context, timestamp, anonymousId, integrations, messageId) + elif specType == 'alias': + analytics.alias(previousId, userId, context, timestamp, integrations, messageId) + elif specType == 'group': + analytics.group(userId, groupId, traits, context, timestamp, anonymousId, integrations, messageId) + else: + raise Exception + except Exception as e: + logger.exception(e) + finally: + analytics.flush() + + +def log_config(): + # Create a logger object + logger = logging.getLogger(os.getenv('APP_NAME')) + logger.setLevel(logging.DEBUG) + + # Create a file handler to log messages to a file + log_directory = f"{os.getenv('LOG_DIRECTORY')}/{datetime.now().strftime('%Y%m%d')}" + os.makedirs(log_directory, exist_ok=True) + log_filename = f"{os.getenv('LOG_FILENAME_SUFFIX')}_{datetime.now().strftime('%H')}00.log" + + # Create a rotating file handler + handler = RotatingFileHandler(os.path.join(log_directory, log_filename), + mode='a', maxBytes=1024*1024, backupCount=100) + handler.setLevel(logging.DEBUG) + + # Define the log message format + formatter = logging.Formatter(os.getenv('LOG_FORMAT')) + handler.setFormatter(formatter) + + # Attach the handler to the logger + logger.addHandler(handler) + + return logger + + +if __name__ == '__main__': + run() From 18827f3e14091da5e7e740a0b0d79cb6cdd07f3c Mon Sep 17 00:00:00 2001 From: neelkanth-kaushik Date: Tue, 25 Mar 2025 11:56:23 +0530 Subject: [PATCH 2/3] Update the code according to comments to PR --- .gitignore | 3 +- e2e-test/.env | 2 - e2e-test/README.md | 45 ++++++++++++--------- e2e-test/src/cli.py | 95 +++++++++++++++++++++++++-------------------- 4 files changed, 80 insertions(+), 65 deletions(-) diff --git a/.gitignore b/.gitignore index 767be8d3..ebecd38d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ build .vscode/ .idea/ .python-version -.venv \ No newline at end of file +.venv +.DS_Store \ No newline at end of file diff --git a/e2e-test/.env b/e2e-test/.env index daa06fa2..35228c47 100644 --- a/e2e-test/.env +++ b/e2e-test/.env @@ -1,6 +1,4 @@ APP_NAME='e2e-test' DEBUG_MODE = False SEND_EVENTS = True -LOG_DIRECTORY='logs' -LOG_FILENAME_SUFFIX='e2e-test' LOG_FORMAT='%(asctime)s - %(name)s - %(levelname)s - %(message)s' \ No newline at end of file diff --git a/e2e-test/README.md b/e2e-test/README.md index 8539a5f9..64882209 100644 --- a/e2e-test/README.md +++ b/e2e-test/README.md @@ -40,49 +40,56 @@ This tool is created for the purpose of E2E Testing. ## Usage Examples with Sample Payloads -### 1. Identify +| Command Option | Type | Description | Required | +|--|--|--|--| +| --writeKey | String | Segment Write Key | Yes | +| --apiHost | String | Custom Host | No | +| --payload | JSON | Event Payload | Yes | + +### Example: Passing Multiple Events as JSON Array + +```bash +$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --apiHost='' --payload='"[{\"anonymousId\":\"507f191e810c89729de960ea\",\"channel\":\"browser\",\"context\":{\"app\":\"ecommerce\"},\"integrations\":{\"All\":false,\"Mixpanel\":true,\"Salesforce\":true},\"messageId\":\"022ty90c-bbac-11e4-8dfc-aa07a5b093q8\",\"traits\":{\"name\":\"Clark Kent\",\"email\":\"clark@example.com\",\"plan\":\"premium\",\"logins\":5,\"address\":{\"street\":\"6th St\",\"city\":\"San Francisco\",\"state\":\"CA\",\"postalCode\":\"94103\",\"country\":\"USA\"}},\"type\":\"identify\",\"userId\":\"AiUGstSDIg\",\"version\":\"2.0\"},{\"messageId\":\"122bb9ui-bbac-11e4-8dfc-aa07z5b098ip\",\"userId\":\"AiUGstSDIg\",\"type\":\"track\",\"event\":\"Course Clicked\",\"context\":{\"page\":{\"path\":\"/academy/\",\"referrer\":\"\",\"search\":\"\",\"title\":\"Analytics Academy\",\"url\":\"https://segment.com/academy/\"}},\"integrations\":{},\"properties\":{\"title\":\"Intro to Analytics\"}}]"' +``` + +### Example: Passing Individual Events + +#### 1. Identify ``` bash -$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c89729de960ea\",\"channel\":\"browser\",\"context\":{\"ip\":\"8.8.8.8\",\"userAgent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36\"},\"integrations\":{\"All\":false,\"Mixpanel\":true,\"Salesforce\":true},\"messageId\":\"022bb90c-bbac-11e4-8dfc-aa07a5b093q8\",\"traits\":{\"name\":\"Clark Kent\",\"email\":\"clark@example.com\",\"plan\":\"premium\",\"logins\":5,\"address\":{\"street\":\"6th St\",\"city\":\"San Francisco\",\"state\":\"CA\",\"postalCode\":\"94103\",\"country\":\"USA\"}},\"type\":\"identify\",\"userId\":\"97980cfea0062\",\"version\":\"2.0\"}"' +$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c89729de960ea\",\"channel\":\"browser\",\"context\":{\"app\":\"ecommerce\"},\"integrations\":{\"All\":false,\"Mixpanel\":true,\"Salesforce\":true},\"messageId\":\"022bb90c-bbac-11e4-8dfc-aa07a5b093q8\",\"traits\":{\"name\":\"Clark Kent\",\"email\":\"clark@example.com\",\"plan\":\"premium\",\"logins\":5,\"address\":{\"street\":\"6th St\",\"city\":\"San Francisco\",\"state\":\"CA\",\"postalCode\":\"94103\",\"country\":\"USA\"}},\"type\":\"identify\",\"userId\":\"97980cfea0062\",\"version\":\"2.0\"}"' ``` -### 2. Track +#### 2. Track ``` bash -$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"messageId\":\"122bb90c-bbac-11e4-8dfc-aa07z5b098ip\",\"userId\":\"AiUGstSDIg\",\"type\":\"track\",\"event\":\"Course Clicked\",\"context\":{\"page\":{\"path\":\"/academy/\",\"referrer\":\"\",\"search\":\"\",\"title\":\"Analytics Academy\",\"url\":\"https://segment.com/academy/\"},\"userAgent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\",\"ip\":\"108.0.78.21\"},\"integrations\":{},\"properties\":{\"title\":\"Intro to Analytics\"}}"' +$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"messageId\":\"122bb90c-bbac-11e4-8dfc-aa07z5b098ip\",\"userId\":\"AiUGstSDIg\",\"type\":\"track\",\"event\":\"Course Clicked\",\"context\":{\"page\":{\"path\":\"/academy/\",\"referrer\":\"\",\"search\":\"\",\"title\":\"Analytics Academy\",\"url\":\"https://segment.com/academy/\"}},\"integrations\":{},\"properties\":{\"title\":\"Intro to Analytics\"}}"' ``` -### 3. Page +#### 3. Page ``` bash -$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c19729de860ea\",\"channel\":\"browser\",\"context\":{\"ip\":\"8.8.8.8\",\"userAgent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36\"},\"integrations\":{\"All\":true,\"Mixpanel\":false,\"Salesforce\":false},\"messageId\":\"022bb90c-bbac-11e8-8dfc-aa07a5b090ol\",\"name\":\"Home\",\"properties\":{\"title\":\"Welcome | Initech\",\"url\":\"http://www.example.com\"},\"type\":\"page\",\"userId\":\"97980cfea0067\"}"' +$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c19729de860ea\",\"channel\":\"browser\",\"integrations\":{\"All\":true,\"Mixpanel\":false,\"Salesforce\":false},\"messageId\":\"022bb90c-bbac-11e8-8dfc-aa07a5b090ol\",\"name\":\"Home\",\"properties\":{\"title\":\"Welcome | Initech\",\"url\":\"http://www.example.com\"},\"type\":\"page\",\"userId\":\"97980cfea0067\"}"' ``` -### 4. Screen +#### 4. Screen ``` bash -$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c19729de860ea\",\"channel\":\"browser\",\"context\":{\"ip\":\"8.8.8.8\",\"userAgent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36\"},\"integrations\":{\"All\":true,\"Mixpanel\":false,\"Salesforce\":false},\"messageId\":\"022bb90c-bbac-11e4-8dfc-aa07a5b090op\",\"name\":\"Registration\",\"properties\":{\"title\":\"Welcome | Initech\",\"url\":\"http://www.example.com\"},\"type\":\"screen\",\"userId\":\"97980cfea0067\"}"' +$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c19729de860ea\",\"channel\":\"browser\",\"integrations\":{\"All\":true,\"Mixpanel\":false,\"Salesforce\":false},\"messageId\":\"022bb90c-bbac-11e8-8dfc-aa07a5b090ol\",\"name\":\"Registration\",\"properties\":{\"title\":\"Welcome | Initech\",\"url\":\"http://www.example.com\"},\"type\":\"Screen\",\"userId\":\"97980cfea0067\"}"' ``` -### 5. Alias +#### 5. Alias ``` bash -$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c19729de800ea\",\"channel\":\"browser\",\"context\":{\"ip\":\"8.8.8.8\",\"userAgent\":\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36\"},\"integrations\":{\"All\":true,\"Mixpanel\":false,\"Salesforce\":false},\"messageId\":\"022bb90c-bbac-11e4-8dfc-aa07u5b093lk\",\"previousId\":\"12345-239239-239239-23923\",\"type\":\"alias\",\"userId\":\"507f191e81\",\"version\":\"1.9\"}"' +$ e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c19729de800ea\",\"channel\":\"browser\",\"integrations\":{\"All\":true,\"Mixpanel\":false,\"Salesforce\":false},\"messageId\":\"022bb90c-bbac-11e4-8dfc-aa07u5b093lk\",\"previousId\":\"12345-239239-239239-23923\",\"type\":\"alias\",\"userId\":\"507f191e81\",\"version\":\"1.9\"}"' ``` -### 6. Group +#### 6. Group ``` bash -% e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"type\":\"group\",\"groupId\":\"0e8c78ea9d97a7b8185e8632\",\"userId\":\"EMP12345\",\"traits\":{\"name\":\"Initech\",\"industry\":\"Technology\",\"employees\":329,\"plan\":\"enterprise\",\"total billed\":830}}"' +% e2e-test:run --writeKey='YOUR_WRITE_KEY' --payload='"{\"anonymousId\":\"507f191e810c19729de800ea\",\"channel\":\"browser\",\"integrations\":{\"All\":true,\"Mixpanel\":false,\"Salesforce\":false},\"messageId\":\"022bb90c-bbac-11e4-8dfc-aa07u5b093lk\",\"previousId\":\"12345-239239-239239-23923\",\"type\":\"alias\",\"userId\":\"507f191e81\",\"version\":\"1.9\"}"' ``` -## Error Logs - - 1. Errors logs are written under ```logs```directory. - 2. Each day, a new log directory is created under ```logs``` in ```YearMonthDay``` format. - 3. Log files are named as ```e2e-test_.log``` - 4. Log file is rotated is maximum allowed file size is reached. - ## Configuration Options A few configuration options are available in the ```.env``` file. \ No newline at end of file diff --git a/e2e-test/src/cli.py b/e2e-test/src/cli.py index 7ee6083c..b5bded3a 100644 --- a/e2e-test/src/cli.py +++ b/e2e-test/src/cli.py @@ -3,9 +3,8 @@ import os import sys import logging -from logging.handlers import RotatingFileHandler import json -from datetime import datetime +from collections.abc import Iterable load_dotenv() @@ -16,9 +15,14 @@ @click.command() @click.option('--writeKey', type=str, help='Segment write key') +@click.option('--apiHost', type=str, help='Custom host') @click.option('--payload', type=str, help='A JSON string that specifies the event payload.') -def run(writekey, payload): +def run(writekey, payload, apihost=None): analytics.write_key = writekey + + if apihost is not None: + analytics.host = apihost # Set custom host + analytics.debug = os.getenv('DEBUG_MODE') analytics.send = os.getenv('SEND_EVENTS') logger = log_config() @@ -26,38 +30,50 @@ def run(writekey, payload): try: # Decode the JSON payload decodedJson = json.loads(payload) - data = json.loads(decodedJson) - - specType = data.get('type') if data.get('type') is not None else None - messageId = data.get('messageId') if data.get('messageId') is not None else None - userId = data.get('userId') if data.get('userId') is not None else '' - eventName = data.get('event') if data.get('event') is not None else None - traits = data.get('traits') if data.get('traits') is not None else None - properties = data.get('properties') if data.get('properties') is not None else None - context = data.get('context') if data.get('context') is not None else None - integrations = data.get('integrations') if data.get('integrations') is not None else None - groupId = data.get('groupId') if data.get('groupId') is not None else None - pageOrScreenName = data.get('name') if data.get('name') is not None else None - pageOrScreenCategory = data.get('category') if data.get('category') is not None else None - timestamp = data.get('timestamp') if data.get('timestamp') is not None else None - anonymousId = data.get('anonymousId') if data.get('anonymousId') is not None else '' - previousId = data.get('previousId') if data.get('previousId') is not None else None - if specType == 'identify': - analytics.identify(userId, traits, context, timestamp, anonymousId, integrations, messageId) - elif specType == 'track': - analytics.track(userId, eventName, properties, context, timestamp, anonymousId, integrations, messageId) - elif specType == 'page': - analytics.page(userId, pageOrScreenCategory, pageOrScreenName, properties, - context, timestamp, anonymousId, integrations, messageId) - elif specType == 'screen': - analytics.screen(userId, pageOrScreenCategory, pageOrScreenName, properties, - context, timestamp, anonymousId, integrations, messageId) - elif specType == 'alias': - analytics.alias(previousId, userId, context, timestamp, integrations, messageId) - elif specType == 'group': - analytics.group(userId, groupId, traits, context, timestamp, anonymousId, integrations, messageId) - else: - raise Exception + dataObject = json.loads(decodedJson) + + # To ensure for loop do not raise exception when individual JSON is passed + # we are converting the payload to a List having a single Dictionary as its item + + if not isinstance(dataObject, Iterable): # Check if dataObject is non-iterable + dataObject = [dataObject] + elif isinstance(dataObject, dict): # Check if dataObject is a dictionary + dataObject = [dataObject] + + # Iterate over each item in the payload JSON + for data in dataObject: + + specType = data.get('type') if data.get('type') is not None else None + messageId = data.get('messageId') if data.get('messageId') is not None else None + userId = data.get('userId') if data.get('userId') is not None else '' + eventName = data.get('event') if data.get('event') is not None else None + traits = data.get('traits') if data.get('traits') is not None else None + properties = data.get('properties') if data.get('properties') is not None else None + context = data.get('context') if data.get('context') is not None else None + integrations = data.get('integrations') if data.get('integrations') is not None else None + groupId = data.get('groupId') if data.get('groupId') is not None else None + pageOrScreenName = data.get('name') if data.get('name') is not None else None + pageOrScreenCategory = data.get('category') if data.get('category') is not None else None + timestamp = data.get('timestamp') if data.get('timestamp') is not None else None + anonymousId = data.get('anonymousId') if data.get('anonymousId') is not None else '' + previousId = data.get('previousId') if data.get('previousId') is not None else None + + if specType == 'identify': + analytics.identify(userId, traits, context, timestamp, anonymousId, integrations, messageId) + elif specType == 'track': + analytics.track(userId, eventName, properties, context, timestamp, anonymousId, integrations, messageId) + elif specType == 'page': + analytics.page(userId, pageOrScreenCategory, pageOrScreenName, properties, + context, timestamp, anonymousId, integrations, messageId) + elif specType == 'screen': + analytics.screen(userId, pageOrScreenCategory, pageOrScreenName, properties, + context, timestamp, anonymousId, integrations, messageId) + elif specType == 'alias': + analytics.alias(previousId, userId, context, timestamp, integrations, messageId) + elif specType == 'group': + analytics.group(userId, groupId, traits, context, timestamp, anonymousId, integrations, messageId) + else: + raise Exception except Exception as e: logger.exception(e) finally: @@ -69,14 +85,7 @@ def log_config(): logger = logging.getLogger(os.getenv('APP_NAME')) logger.setLevel(logging.DEBUG) - # Create a file handler to log messages to a file - log_directory = f"{os.getenv('LOG_DIRECTORY')}/{datetime.now().strftime('%Y%m%d')}" - os.makedirs(log_directory, exist_ok=True) - log_filename = f"{os.getenv('LOG_FILENAME_SUFFIX')}_{datetime.now().strftime('%H')}00.log" - - # Create a rotating file handler - handler = RotatingFileHandler(os.path.join(log_directory, log_filename), - mode='a', maxBytes=1024*1024, backupCount=100) + handler = logging.StreamHandler() handler.setLevel(logging.DEBUG) # Define the log message format From 532134767b94f85cd7415edde22180036897c0ac Mon Sep 17 00:00:00 2001 From: neelkanth-kaushik Date: Tue, 8 Apr 2025 10:58:41 +0530 Subject: [PATCH 3/3] Fixed - ModuleNotFoundError: No module named 'src' when running e2e-test:run --- e2e-test/setup.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/e2e-test/setup.py b/e2e-test/setup.py index b7333405..6752f569 100644 --- a/e2e-test/setup.py +++ b/e2e-test/setup.py @@ -1,9 +1,10 @@ -from setuptools import setup +from setuptools import setup, find_packages setup( name='e2e-test', version='0.1.0', - py_modules=['e2e-test'], + packages=find_packages(), + include_package_data=True, install_requires=[ 'click', 'python-dotenv', 'python-dateutil', 'requests', 'PyJWT', 'backoff' ],