Coverage for chalicelib/core/assist.py: 32%
193 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 12:56 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 12:56 +0000
1import logging
2from os import access, R_OK
3from os.path import exists as path_exists, getsize
5import jwt
6import requests
7from decouple import config
8from fastapi import HTTPException, status
10import schemas
11from chalicelib.core import projects
12from chalicelib.utils.TimeUTC import TimeUTC
13from chalicelib.utils.log import sanitize
15logger = logging.getLogger(__name__)
17ASSIST_KEY = config("ASSIST_KEY")
18ASSIST_URL = config("ASSIST_URL") % ASSIST_KEY
21def get_live_sessions_ws_user_id(project_id, user_id):
22 data = {
23 "filter": {"userId": user_id} if user_id else {}
24 }
25 return __get_live_sessions_ws(project_id=project_id, data=data)
28def get_live_sessions_ws_test_id(project_id, test_id):
29 data = {
30 "filter": {
31 'uxtId': test_id,
32 'operator': 'is'
33 }
34 }
35 return __get_live_sessions_ws(project_id=project_id, data=data)
38def get_live_sessions_ws(project_id, body: schemas.LiveSessionsSearchPayloadSchema):
39 data = {
40 "filter": {},
41 "pagination": {"limit": body.limit, "page": body.page},
42 "sort": {"key": body.sort, "order": body.order}
43 }
44 for f in body.filters:
45 if f.name == schemas.LiveFilterType.METADATA: 45 ↛ 46line 45 didn't jump to line 46 because the condition on line 45 was never true
46 data["filter"][f.source] = {"values": f.value, "operator": f.operator}
48 else:
49 data["filter"][f.name] = {"values": f.value, "operator": f.operator}
50 return __get_live_sessions_ws(project_id=project_id, data=data)
53def __get_live_sessions_ws(project_id, data):
54 project_key = projects.get_project_key(project_id)
55 try:
56 results = requests.post(ASSIST_URL + config("assist") + f"/{project_key}",
57 json=data, timeout=config("assistTimeout", cast=int, default=5))
58 if results.status_code != 200:
59 logger.error(f"!! issue with the peer-server code:{results.status_code} for __get_live_sessions_ws")
60 logger.error(sanitize(results.text))
61 return {"total": 0, "sessions": []}
62 live_peers = results.json().get("data", [])
63 except requests.exceptions.Timeout:
64 logger.error("!! Timeout getting Assist response")
65 live_peers = {"total": 0, "sessions": []}
66 except Exception as e:
67 logger.error("!! Issue getting Live-Assist response")
68 logger.exception(e)
69 logger.error("expected JSON, received:")
70 try:
71 logger.error(sanitize(results.text))
72 except:
73 logger.error("couldn't get response")
74 live_peers = {"total": 0, "sessions": []}
75 _live_peers = live_peers
76 if "sessions" in live_peers: 76 ↛ 78line 76 didn't jump to line 78 because the condition on line 76 was always true
77 _live_peers = live_peers["sessions"]
78 for s in _live_peers: 78 ↛ 79line 78 didn't jump to line 79 because the loop on line 78 never started
79 s["live"] = True
80 s["projectId"] = project_id
81 if "projectID" in s:
82 s.pop("projectID")
83 return live_peers
86def __get_agent_token(project_id, project_key, session_id):
87 iat = TimeUTC.now()
88 return jwt.encode(
89 payload={
90 "projectKey": project_key,
91 "projectId": project_id,
92 "sessionId": session_id,
93 "iat": iat // 1000,
94 "exp": iat // 1000 + config("ASSIST_JWT_EXPIRATION", cast=int) + TimeUTC.get_utc_offset() // 1000,
95 "iss": config("JWT_ISSUER"),
96 "aud": f"openreplay:agent"
97 },
98 key=config("ASSIST_JWT_SECRET"),
99 algorithm=config("JWT_ALGORITHM")
100 )
103def get_live_session_by_id(project_id, session_id):
104 project_key = projects.get_project_key(project_id)
105 try:
106 results = requests.get(ASSIST_URL + config("assist") + f"/{project_key}/{session_id}",
107 timeout=config("assistTimeout", cast=int, default=5))
108 if results.status_code != 200:
109 logger.error(f"!! issue with the peer-server code:{results.status_code} for get_live_session_by_id")
110 logger.error(sanitize(results.text))
111 return None
112 results = results.json().get("data")
113 if results is None:
114 return None
115 results["live"] = True
116 results["agentToken"] = __get_agent_token(project_id=project_id, project_key=project_key, session_id=session_id)
117 except requests.exceptions.Timeout:
118 logger.error("!! Timeout getting Assist response")
119 return None
120 except Exception as e:
121 logger.error("!! Issue getting Assist response")
122 logger.exception(e)
123 logger.error("expected JSON, received:")
124 try:
125 logger.error(sanitize(results.text))
126 except:
127 logger.error("couldn't get response")
128 return None
129 return results
132def is_live(project_id, session_id, project_key=None):
133 if project_key is None:
134 project_key = projects.get_project_key(project_id)
135 try:
136 results = requests.get(ASSIST_URL + config("assistList") + f"/{project_key}/{session_id}",
137 timeout=config("assistTimeout", cast=int, default=5))
138 if results.status_code != 200:
139 logger.error(f"!! issue with the peer-server code:{results.status_code} for is_live")
140 logger.error(sanitize(results.text))
141 return False
142 results = results.json().get("data")
143 except requests.exceptions.Timeout:
144 logger.error("!! Timeout getting Assist response")
145 return False
146 except Exception as e:
147 logger.error("!! Issue getting Assist response")
148 logger.exception(e)
149 logger.error("expected JSON, received:")
150 try:
151 logger.error(sanitize(results.text))
152 except:
153 logger.error("couldn't get response")
154 return False
155 return str(session_id) == results
158def autocomplete(project_id, q: str, key: str = None):
159 project_key = projects.get_project_key(project_id)
160 params = {"q": q}
161 if key:
162 params["key"] = key
163 try:
164 results = requests.get(
165 ASSIST_URL + config("assistList") + f"/{project_key}/autocomplete",
166 params=params, timeout=config("assistTimeout", cast=int, default=5))
167 if results.status_code != 200:
168 logger.error(f"!! issue with the peer-server code:{results.status_code} for autocomplete")
169 logger.error(sanitize(results.text))
170 return {"errors": [f"Something went wrong wile calling assist:{results.text}"]}
171 results = results.json().get("data", [])
172 except requests.exceptions.Timeout:
173 logger.error("!! Timeout getting Assist response")
174 return {"errors": ["Assist request timeout"]}
175 except Exception as e:
176 logger.error("!! Issue getting Assist response")
177 logger.exception(e)
178 logger.error("expected JSON, received:")
179 try:
180 logger.error(sanitize(results.text))
181 except:
182 logger.error("couldn't get response")
183 return {"errors": ["Something went wrong wile calling assist"]}
184 for r in results:
185 r["type"] = __change_keys(r["type"])
186 return {"data": results}
189def __get_efs_path():
190 efs_path = config("FS_DIR")
191 if not path_exists(efs_path):
192 raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"EFS not found in path: {efs_path}")
194 if not access(efs_path, R_OK):
195 raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
196 detail=f"EFS found under: {efs_path}; but it is not readable, please check permissions")
197 return efs_path
200def __get_mob_path(project_id, session_id):
201 params = {"projectId": project_id, "sessionId": session_id}
202 return config("EFS_SESSION_MOB_PATTERN", default="%(sessionId)s") % params
205def get_raw_mob_by_id(project_id, session_id):
206 efs_path = __get_efs_path()
207 path_to_file = efs_path + "/" + __get_mob_path(project_id=project_id, session_id=session_id)
208 if path_exists(path_to_file):
209 if not access(path_to_file, R_OK):
210 raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
211 detail=f"Replay file found under: {efs_path};" +
212 " but it is not readable, please check permissions")
213 # getsize return size in bytes, UNPROCESSED_MAX_SIZE is in Kb
214 if (getsize(path_to_file) / 1000) >= config("UNPROCESSED_MAX_SIZE", cast=int, default=200 * 1000):
215 raise HTTPException(status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, detail="Replay file too large")
216 return path_to_file
218 return None
221def __get_devtools_path(project_id, session_id):
222 params = {"projectId": project_id, "sessionId": session_id}
223 return config("EFS_DEVTOOLS_MOB_PATTERN", default="%(sessionId)s") % params
226def get_raw_devtools_by_id(project_id, session_id):
227 efs_path = __get_efs_path()
228 path_to_file = efs_path + "/" + __get_devtools_path(project_id=project_id, session_id=session_id)
229 if path_exists(path_to_file):
230 if not access(path_to_file, R_OK):
231 raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,
232 detail=f"Devtools file found under: {efs_path};"
233 " but it is not readable, please check permissions")
235 return path_to_file
237 return None
240def session_exists(project_id, session_id):
241 project_key = projects.get_project_key(project_id)
242 try:
243 results = requests.get(ASSIST_URL + config("assist") + f"/{project_key}/{session_id}",
244 timeout=config("assistTimeout", cast=int, default=5))
245 if results.status_code != 200:
246 logger.error(f"!! issue with the peer-server code:{results.status_code} for session_exists")
247 logger.error(sanitize(results.text))
248 return None
249 results = results.json().get("data")
250 if results is None:
251 return False
252 return True
253 except requests.exceptions.Timeout:
254 logger.error("!! Timeout getting Assist response")
255 return False
256 except Exception as e:
257 logger.error("!! Issue getting Assist response")
258 logger.exception(e)
259 logger.error("expected JSON, received:")
260 try:
261 logger.error(sanitize(results.text))
262 except:
263 logger.error("couldn't get response")
264 return False
267def __change_keys(key):
268 return {
269 "PAGETITLE": schemas.LiveFilterType.PAGE_TITLE.value,
270 "ACTIVE": "active",
271 "LIVE": "live",
272 "SESSIONID": schemas.LiveFilterType.SESSION_ID.value,
273 "METADATA": schemas.LiveFilterType.METADATA.value,
274 "USERID": schemas.LiveFilterType.USER_ID.value,
275 "USERUUID": schemas.LiveFilterType.USER_UUID.value,
276 "PROJECTKEY": "projectKey",
277 "REVID": schemas.LiveFilterType.REV_ID.value,
278 "TIMESTAMP": "timestamp",
279 "TRACKERVERSION": schemas.LiveFilterType.TRACKER_VERSION.value,
280 "ISSNIPPET": "isSnippet",
281 "USEROS": schemas.LiveFilterType.USER_OS.value,
282 "USERBROWSER": schemas.LiveFilterType.USER_BROWSER.value,
283 "USERBROWSERVERSION": schemas.LiveFilterType.USER_BROWSER_VERSION.value,
284 "USERDEVICE": schemas.LiveFilterType.USER_DEVICE.value,
285 "USERDEVICETYPE": schemas.LiveFilterType.USER_DEVICE_TYPE.value,
286 "USERCOUNTRY": schemas.LiveFilterType.USER_COUNTRY.value,
287 "PROJECTID": "projectId"
288 }.get(key.upper(), key)