Coverage for chalicelib/core/errors/errors_ch.py: 43%

51 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 12:56 +0000

1import json 

2import logging 

3 

4from chalicelib.core.sourcemaps import sourcemaps 

5from chalicelib.utils import ch_client, pg_client 

6from chalicelib.utils import helper 

7from chalicelib.utils.TimeUTC import TimeUTC 

8 

9logger = logging.getLogger(__name__) 

10 

11 

12def get(error_id, project_id) -> dict | None: 

13 with ch_client.ClickHouseClient() as cur: 

14 # @formatter:off 

15 query = """SELECT error_id, 

16 project_id, 

17 `$properties`.source AS source, 

18 'ERROR' AS name, 

19 `$properties`.message AS message, 

20 `$properties`.payload AS payload, 

21 stacktrace, 

22 stacktrace_parsed_at 

23 FROM product_analytics.events 

24 LEFT JOIN (SELECT error_id, stacktrace, stacktrace_parsed_at 

25 FROM experimental.parsed_errors 

26 WHERE project_id = toUInt16(%(project_id)s) 

27 AND error_id = %(error_id)s 

28 AND NOT is_deleted 

29 ORDER BY stacktrace_parsed_at DESC 

30 LIMIT 1) AS parsed USING (error_id) 

31 WHERE "$event_name" = 'ERROR' 

32 AND error_id = %(error_id)s 

33 AND events.project_id = toUInt16(%(project_id)s)  

34 LIMIT 1;""" 

35 # @formatter:on 

36 rows = cur.execute(query, {"error_id": error_id, "project_id": project_id}) 

37 if len(rows) == 0: 37 ↛ 39line 37 didn't jump to line 39

38 return None 

39 return helper.dict_to_camel_case(rows[0]) 

40 

41 

42def __save_stacktrace(project_id, error_id, data): 

43 with ch_client.ClickHouseClient() as cur: 

44 query = f"""INSERT INTO experimental.parsed_errors(project_id, error_id, stacktrace)  

45 VALUES (%(project_id)s,%(error_id)s,%(data)s);""" 

46 params = {"project_id": project_id, "error_id": error_id, "data": json.dumps(data)} 

47 cur.execute(query=query, parameters=params) 

48 

49 

50def get_trace(project_id, error_id): 

51 error = get(error_id=error_id, project_id=project_id) 

52 if error is None: 52 ↛ 54line 52 didn't jump to line 54 because the condition on line 52 was always true

53 return {"errors": ["error not found"]} 

54 if error.get("source", "") != "js_exception": 

55 return {"errors": ["this source of errors doesn't have a sourcemap"]} 

56 if error.get("payload") is None or error.get("payload") == "": 

57 return {"errors": ["null payload"]} 

58 if error.get("stacktrace") is not None and error.get("stacktrace") != "": 

59 return {"sourcemapUploaded": True, 

60 "trace": json.loads(error.get("stacktrace")), 

61 "preparsed": True} 

62 

63 payload = json.loads(error["payload"]) if isinstance(error["payload"], str) else error["payload"] 

64 trace, all_exists = sourcemaps.get_traces_group(project_id=project_id, payload=payload) 

65 if all_exists: 

66 __save_stacktrace(project_id=project_id, error_id=error_id, data=trace) 

67 return {"sourcemapUploaded": all_exists, 

68 "trace": trace, 

69 "preparsed": False} 

70 

71 

72# TODO: Delete this if UI is not calling the corresponding endpoint 

73def get_sessions(start_date, end_date, project_id, user_id, error_id): 

74 extra_constraints = ["s.project_id = %(project_id)s", 

75 "s.start_ts >= %(startDate)s", 

76 "s.start_ts <= %(endDate)s", 

77 "e.error_id = %(error_id)s"] 

78 if start_date is None: 78 ↛ 79line 78 didn't jump to line 79 because the condition on line 78 was never true

79 start_date = TimeUTC.now(-7) 

80 if end_date is None: 80 ↛ 81line 80 didn't jump to line 81 because the condition on line 80 was never true

81 end_date = TimeUTC.now() 

82 

83 params = { 

84 "startDate": start_date, 

85 "endDate": end_date, 

86 "project_id": project_id, 

87 "userId": user_id, 

88 "error_id": error_id} 

89 with pg_client.PostgresClient() as cur: 

90 query = cur.mogrify( 

91 f"""SELECT s.project_id, 

92 s.session_id::text AS session_id, 

93 s.user_uuid, 

94 s.user_id, 

95 s.user_agent, 

96 s.user_os, 

97 s.user_browser, 

98 s.user_device, 

99 s.user_country, 

100 s.start_ts, 

101 s.duration, 

102 s.events_count, 

103 s.pages_count, 

104 s.errors_count, 

105 s.issue_types, 

106 COALESCE((SELECT TRUE 

107 FROM public.user_favorite_sessions AS fs 

108 WHERE s.session_id = fs.session_id 

109 AND fs.user_id = %(userId)s LIMIT 1), FALSE) AS favorite, 

110 COALESCE((SELECT TRUE 

111 FROM public.user_viewed_sessions AS fs 

112 WHERE s.session_id = fs.session_id 

113 AND fs.user_id = %(userId)s LIMIT 1), FALSE) AS viewed 

114 FROM public.sessions AS s INNER JOIN events.errors AS e USING (session_id) 

115 WHERE {" AND ".join(extra_constraints)} 

116 ORDER BY s.start_ts DESC;""", 

117 params) 

118 cur.execute(query=query) 

119 sessions_list = [] 

120 total = cur.rowcount 

121 row = cur.fetchone() 

122 while row is not None and len(sessions_list) < 100: 

123 sessions_list.append(row) 

124 row = cur.fetchone() 

125 

126 return { 

127 'total': total, 

128 'sessions': helper.list_to_camel_case(sessions_list) 

129 }