openreplay/api/chalicelib/core/assist.py
Kraiem Taha Yassine f7ddf82591
Dev (#2894)
* feat(chalice): autocomplete return top 10 with stats

* fix(chalice): fixed autocomplete top 10 meta-filters

* fix(chalice): multiple migration fixes
refactor(chalice): refactored ch-sessions code
2024-12-19 18:21:30 +01:00

287 lines
11 KiB
Python

import logging
from os import access, R_OK
from os.path import exists as path_exists, getsize
import jwt
import requests
from decouple import config
from fastapi import HTTPException, status
import schemas
from chalicelib.core import projects
from chalicelib.utils.TimeUTC import TimeUTC
logger = logging.getLogger(__name__)
ASSIST_KEY = config("ASSIST_KEY")
ASSIST_URL = config("ASSIST_URL") % ASSIST_KEY
def get_live_sessions_ws_user_id(project_id, user_id):
data = {
"filter": {"userId": user_id} if user_id else {}
}
return __get_live_sessions_ws(project_id=project_id, data=data)
def get_live_sessions_ws_test_id(project_id, test_id):
data = {
"filter": {
'uxtId': test_id,
'operator': 'is'
}
}
return __get_live_sessions_ws(project_id=project_id, data=data)
def get_live_sessions_ws(project_id, body: schemas.LiveSessionsSearchPayloadSchema):
data = {
"filter": {},
"pagination": {"limit": body.limit, "page": body.page},
"sort": {"key": body.sort, "order": body.order}
}
for f in body.filters:
if f.type == schemas.LiveFilterType.METADATA:
data["filter"][f.source] = {"values": f.value, "operator": f.operator}
else:
data["filter"][f.type] = {"values": f.value, "operator": f.operator}
return __get_live_sessions_ws(project_id=project_id, data=data)
def __get_live_sessions_ws(project_id, data):
project_key = projects.get_project_key(project_id)
try:
results = requests.post(ASSIST_URL + config("assist") + f"/{project_key}",
json=data, timeout=config("assistTimeout", cast=int, default=5))
if results.status_code != 200:
logger.error(f"!! issue with the peer-server code:{results.status_code} for __get_live_sessions_ws")
logger.error(results.text)
return {"total": 0, "sessions": []}
live_peers = results.json().get("data", [])
except requests.exceptions.Timeout:
logger.error("!! Timeout getting Assist response")
live_peers = {"total": 0, "sessions": []}
except Exception as e:
logger.error("!! Issue getting Live-Assist response")
logger.exception(e)
logger.error("expected JSON, received:")
try:
logger.error(results.text)
except:
logger.error("couldn't get response")
live_peers = {"total": 0, "sessions": []}
_live_peers = live_peers
if "sessions" in live_peers:
_live_peers = live_peers["sessions"]
for s in _live_peers:
s["live"] = True
s["projectId"] = project_id
if "projectID" in s:
s.pop("projectID")
return live_peers
def __get_agent_token(project_id, project_key, session_id):
iat = TimeUTC.now()
return jwt.encode(
payload={
"projectKey": project_key,
"projectId": project_id,
"sessionId": session_id,
"iat": iat // 1000,
"exp": iat // 1000 + config("ASSIST_JWT_EXPIRATION", cast=int) + TimeUTC.get_utc_offset() // 1000,
"iss": config("JWT_ISSUER"),
"aud": f"openreplay:agent"
},
key=config("ASSIST_JWT_SECRET"),
algorithm=config("JWT_ALGORITHM")
)
def get_live_session_by_id(project_id, session_id):
project_key = projects.get_project_key(project_id)
try:
results = requests.get(ASSIST_URL + config("assist") + f"/{project_key}/{session_id}",
timeout=config("assistTimeout", cast=int, default=5))
if results.status_code != 200:
logger.error(f"!! issue with the peer-server code:{results.status_code} for get_live_session_by_id")
logger.error(results.text)
return None
results = results.json().get("data")
if results is None:
return None
results["live"] = True
results["agentToken"] = __get_agent_token(project_id=project_id, project_key=project_key, session_id=session_id)
except requests.exceptions.Timeout:
logger.error("!! Timeout getting Assist response")
return None
except Exception as e:
logger.error("!! Issue getting Assist response")
logger.exception(e)
logger.error("expected JSON, received:")
try:
logger.error(results.text)
except:
logger.error("couldn't get response")
return None
return results
def is_live(project_id, session_id, project_key=None):
if project_key is None:
project_key = projects.get_project_key(project_id)
try:
results = requests.get(ASSIST_URL + config("assistList") + f"/{project_key}/{session_id}",
timeout=config("assistTimeout", cast=int, default=5))
if results.status_code != 200:
logger.error(f"!! issue with the peer-server code:{results.status_code} for is_live")
logger.error(results.text)
return False
results = results.json().get("data")
except requests.exceptions.Timeout:
logger.error("!! Timeout getting Assist response")
return False
except Exception as e:
logger.error("!! Issue getting Assist response")
logger.exception(e)
logger.error("expected JSON, received:")
try:
logger.error(results.text)
except:
logger.error("couldn't get response")
return False
return str(session_id) == results
def autocomplete(project_id, q: str, key: str = None):
project_key = projects.get_project_key(project_id)
params = {"q": q}
if key:
params["key"] = key
try:
results = requests.get(
ASSIST_URL + config("assistList") + f"/{project_key}/autocomplete",
params=params, timeout=config("assistTimeout", cast=int, default=5))
if results.status_code != 200:
logger.error(f"!! issue with the peer-server code:{results.status_code} for autocomplete")
logger.error(results.text)
return {"errors": [f"Something went wrong wile calling assist:{results.text}"]}
results = results.json().get("data", [])
except requests.exceptions.Timeout:
logger.error("!! Timeout getting Assist response")
return {"errors": ["Assist request timeout"]}
except Exception as e:
logger.error("!! Issue getting Assist response")
logger.exception(e)
logger.error("expected JSON, received:")
try:
logger.error(results.text)
except:
logger.error("couldn't get response")
return {"errors": ["Something went wrong wile calling assist"]}
for r in results:
r["type"] = __change_keys(r["type"])
return {"data": results}
def __get_efs_path():
efs_path = config("FS_DIR")
if not path_exists(efs_path):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"EFS not found in path: {efs_path}")
if not access(efs_path, R_OK):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
detail=f"EFS found under: {efs_path}; but it is not readable, please check permissions")
return efs_path
def __get_mob_path(project_id, session_id):
params = {"projectId": project_id, "sessionId": session_id}
return config("EFS_SESSION_MOB_PATTERN", default="%(sessionId)s") % params
def get_raw_mob_by_id(project_id, session_id):
efs_path = __get_efs_path()
path_to_file = efs_path + "/" + __get_mob_path(project_id=project_id, session_id=session_id)
if path_exists(path_to_file):
if not access(path_to_file, R_OK):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Replay file found under: {efs_path};" +
" but it is not readable, please check permissions")
# getsize return size in bytes, UNPROCESSED_MAX_SIZE is in Kb
if (getsize(path_to_file) / 1000) >= config("UNPROCESSED_MAX_SIZE", cast=int, default=200 * 1000):
raise HTTPException(status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, detail="Replay file too large")
return path_to_file
return None
def __get_devtools_path(project_id, session_id):
params = {"projectId": project_id, "sessionId": session_id}
return config("EFS_DEVTOOLS_MOB_PATTERN", default="%(sessionId)s") % params
def get_raw_devtools_by_id(project_id, session_id):
efs_path = __get_efs_path()
path_to_file = efs_path + "/" + __get_devtools_path(project_id=project_id, session_id=session_id)
if path_exists(path_to_file):
if not access(path_to_file, R_OK):
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Devtools file found under: {efs_path};"
" but it is not readable, please check permissions")
return path_to_file
return None
def session_exists(project_id, session_id):
project_key = projects.get_project_key(project_id)
try:
results = requests.get(ASSIST_URL + config("assist") + f"/{project_key}/{session_id}",
timeout=config("assistTimeout", cast=int, default=5))
if results.status_code != 200:
logger.error(f"!! issue with the peer-server code:{results.status_code} for session_exists")
logger.error(results.text)
return None
results = results.json().get("data")
if results is None:
return False
return True
except requests.exceptions.Timeout:
logger.error("!! Timeout getting Assist response")
return False
except Exception as e:
logger.error("!! Issue getting Assist response")
logger.exception(e)
logger.error("expected JSON, received:")
try:
logger.error(results.text)
except:
logger.error("couldn't get response")
return False
def __change_keys(key):
return {
"PAGETITLE": schemas.LiveFilterType.PAGE_TITLE.value,
"ACTIVE": "active",
"LIVE": "live",
"SESSIONID": schemas.LiveFilterType.SESSION_ID.value,
"METADATA": schemas.LiveFilterType.METADATA.value,
"USERID": schemas.LiveFilterType.USER_ID.value,
"USERUUID": schemas.LiveFilterType.USER_UUID.value,
"PROJECTKEY": "projectKey",
"REVID": schemas.LiveFilterType.REV_ID.value,
"TIMESTAMP": "timestamp",
"TRACKERVERSION": schemas.LiveFilterType.TRACKER_VERSION.value,
"ISSNIPPET": "isSnippet",
"USEROS": schemas.LiveFilterType.USER_OS.value,
"USERBROWSER": schemas.LiveFilterType.USER_BROWSER.value,
"USERBROWSERVERSION": schemas.LiveFilterType.USER_BROWSER_VERSION.value,
"USERDEVICE": schemas.LiveFilterType.USER_DEVICE.value,
"USERDEVICETYPE": schemas.LiveFilterType.USER_DEVICE_TYPE.value,
"USERCOUNTRY": schemas.LiveFilterType.USER_COUNTRY.value,
"PROJECTID": "projectId"
}.get(key.upper(), key)