diff --git a/hdx_hapi/endpoints/middleware/mixpanel_tracking_middleware.py b/hdx_hapi/endpoints/middleware/mixpanel_tracking_middleware.py index a0c81543..438e2e2c 100644 --- a/hdx_hapi/endpoints/middleware/mixpanel_tracking_middleware.py +++ b/hdx_hapi/endpoints/middleware/mixpanel_tracking_middleware.py @@ -1,7 +1,7 @@ from fastapi import Request, BackgroundTasks from hdx_hapi.config.config import get_config -from hdx_hapi.endpoints.middleware.util.util import track_api_call +from hdx_hapi.endpoints.middleware.util.util import track_api_call, track_page_view import logging @@ -26,6 +26,11 @@ async def mixpanel_tracking_middleware(request: Request, call_next): else: logger.warning('HDX_MIXPANEL_TOKEN environment variable is not set.') + if request.url.path.startswith('/docs'): + if CONFIG.HDX_MIXPANEL_TOKEN: + background_tasks.add_task(track_page_view, request, response) + else: + logger.warning('HDX_MIXPANEL_TOKEN environment variable is not set.') response.background = background_tasks return response diff --git a/hdx_hapi/endpoints/middleware/util/util.py b/hdx_hapi/endpoints/middleware/util/util.py index 34688025..55e7bf04 100644 --- a/hdx_hapi/endpoints/middleware/util/util.py +++ b/hdx_hapi/endpoints/middleware/util/util.py @@ -9,6 +9,7 @@ logger = logging.getLogger(__name__) + async def track_api_call(request: Request, response: Response): current_url = str(request.url) endpoint = request.url.path @@ -44,19 +45,47 @@ async def track_api_call(request: Request, response: Response): '$os': ua_os, '$browser': ua_browser, '$browser_version': ua_browser_version, - '$current_url': current_url + '$current_url': current_url, } await send_mixpanel_event('api call', distinct_id, mixpanel_dict) +async def track_page_view(request: Request, response: Response): + current_url = str(request.url) + user_agent_string = request.headers.get('user-agent', '') + ip_address = request.headers.get('HTTP_X_REAL_IP', '') + response_code = response.status_code + distinct_id = HashCodeGenerator({'ip': ip_address, 'ua': user_agent_string}).compute_hash() + event_time = time.time() + ua_dict = useragent.Parse(user_agent_string) + ua_os = ua_dict.get('os', {}).get('family') + ua_browser = ua_dict.get('user_agent', {}).get('family') + ua_browser_version = ua_dict.get('user_agent', {}).get('major') + + page_view_dict = { + 'page title': 'HAPI - OpenAPI Docs', + 'time': event_time, + 'server side': True, + 'response code': response_code, + 'user agent': user_agent_string, + 'ip': ip_address, + '$os': ua_os, + '$browser': ua_browser, + '$browser_version': ua_browser_version, + '$current_url': current_url, + } + await send_mixpanel_event('page view', distinct_id, page_view_dict) + + async def send_mixpanel_event(event_name: str, distinct_id: str, event_data: dict): - mixpanel.track(distinct_id, event_name, event_data) + mixpanel.track(distinct_id, event_name, event_data) class HashCodeGenerator(object): """ Works only on simple dictionaries (not nested). At least the specified fields need to not be nested. """ + def __init__(self, src_dict, field_list=None): if not field_list and src_dict: field_list = list(src_dict.keys()) diff --git a/tests/test_analytics/test_api_call_tracking.py b/tests/test_analytics/test_api_call_tracking.py index 9d2e9ecc..8c642975 100644 --- a/tests/test_analytics/test_api_call_tracking.py +++ b/tests/test_analytics/test_api_call_tracking.py @@ -20,7 +20,6 @@ async def test_tracking_endpoint_success(): 'hdx_hapi.endpoints.middleware.util.util.HashCodeGenerator.compute_hash', return_value='123456', ): - async with AsyncClient(app=app, base_url=TEST_BASE_URL) as ac: headers = { 'User-Agent': TEST_USER_AGENT, @@ -49,17 +48,17 @@ async def test_tracking_endpoint_success(): '$current_url': f'{TEST_BASE_URL}/api/v1/themes/3w?admin_level=1&output_format=json', } - send_mixpanel_event_patch.assert_called_once_with( - 'api call', '123456', expected_mixpanel_dict - ), 'Parameters do not match the expected ones' + ( + send_mixpanel_event_patch.assert_called_once_with('api call', '123456', expected_mixpanel_dict), + 'Parameters do not match the expected ones', + ) @pytest.mark.asyncio -async def test_docs_page_not_tracked(): +async def test_docs_page_tracked(): with patch('hdx_hapi.endpoints.middleware.util.util.send_mixpanel_event') as send_mixpanel_event_patch: - async with AsyncClient(app=app, base_url=TEST_BASE_URL) as ac: response = await ac.get('/docs') assert response.status_code == 200 - assert send_mixpanel_event_patch.call_count == 0, 'Docs page should not be tracked as an API call' + assert send_mixpanel_event_patch.call_count == 1, 'Docs page should be tracked as a page view' diff --git a/tests/test_endpoints/test_endpoints_vs_encode_identifier.py b/tests/test_endpoints/test_endpoints_vs_encode_identifier.py index 6776ac80..3a337be4 100644 --- a/tests/test_endpoints/test_endpoints_vs_encode_identifier.py +++ b/tests/test_endpoints/test_endpoints_vs_encode_identifier.py @@ -52,12 +52,11 @@ async def test_encode_identifier(event_loop, refresh_db, enable_hapi_identifier_ # testing the encode identifier endpoint endpoint_router = '/api/v1/encode_identifier' + # it should not be important if app_identifier is passed or not to the endpoint async with AsyncClient(app=app, base_url='http://test') as ac: response = await ac.get(endpoint_router) - assert response.status_code == 200 + assert response.status_code == 422 async with AsyncClient(app=app, base_url='http://test', params=query_parameters) as ac: response = await ac.get(endpoint_router) - assert response.status_code == 200 - response_items = response.json() - assert len(response_items) > 0 + assert response.status_code == 422