Coverage for routers/core.py: 84%
455 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 typing import Union, Optional
4from decouple import config
5from fastapi import Depends, Body, BackgroundTasks
7import schemas
8from chalicelib.core import projects, metadata, reset_password, log_tools, \
9 announcements, weekly_report, assist, mobile, tenants, boarding, notifications, webhook, users, tags
10from chalicelib.core.alerts import alerts
11from chalicelib.core.metrics import custom_metrics
12from chalicelib.core.collaborations.collaboration_msteams import MSTeams
13from chalicelib.core.collaborations.collaboration_slack import Slack
14from chalicelib.core.issue_tracking import github, integrations_global, integrations_manager, \
15 jira_cloud
16from chalicelib.core.log_tools import datadog, newrelic, stackdriver, elasticsearch, \
17 sentry, bugsnag, cloudwatch, sumologic, rollbar
18from chalicelib.core.sourcemaps import sourcemaps
19from or_dependencies import OR_context, OR_role
20from routers.base import get_routers
22public_app, app, app_apikey = get_routers()
24logger = logging.getLogger(__name__)
27@app.get('/{projectId}/autocomplete', tags=["autocomplete"])
28@app.get('/{projectId}/events/search', tags=["events"])
29def global_autocomplete_search(projectId: int, q: Optional[str] = None,
30 type: Union[schemas.FilterType, schemas.EventType,
31 schemas.PerformanceEventType, schemas.FetchFilterType,
32 schemas.GraphqlFilterType, str] = None,
33 key: str = None, live: bool = False,
34 context: schemas.CurrentContext = Depends(OR_context)):
35 if live:
36 return assist.autocomplete(project_id=projectId, q=q,
37 key=key if key is not None else type)
39 return {"data": [], "errors": ["not available for global search"]}
42@app.get('/{projectId}/integrations', tags=["integrations"])
43def get_integrations_status(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
44 data = integrations_global.get_global_integrations_status(tenant_id=context.tenant_id,
45 user_id=context.user_id,
46 project_id=projectId)
47 return {"data": data}
50@app.post('/{projectId}/integrations/{integration}/notify/{webhookId}/{source}/{sourceId}', tags=["integrations"])
51def notify(projectId: int, integration: str, webhookId: int, source: str, sourceId: str,
52 data: schemas.IntegrationNotificationSchema = Body(...),
53 context: schemas.CurrentContext = Depends(OR_context)):
54 comment = None
55 if data.comment:
56 comment = data.comment
58 args = {"tenant_id": context.tenant_id,
59 "user": context.email, "comment": comment, "project_id": projectId,
60 "integration_id": webhookId,
61 "project_name": context.project.name}
62 if integration == schemas.WebhookType.SLACK: 62 ↛ 63line 62 didn't jump to line 63 because the condition on line 62 was never true
63 if source == "sessions":
64 return Slack.share_session(session_id=sourceId, **args)
65 elif source == "errors":
66 return Slack.share_error(error_id=sourceId, **args)
67 elif integration == schemas.WebhookType.MSTEAMS: 67 ↛ 68line 67 didn't jump to line 68 because the condition on line 67 was never true
68 if source == "sessions":
69 return MSTeams.share_session(session_id=sourceId, **args)
70 elif source == "errors":
71 return MSTeams.share_error(error_id=sourceId, **args)
72 return {"data": None}
75@app.get('/integrations/sentry', tags=["integrations"])
76def get_all_sentry(context: schemas.CurrentContext = Depends(OR_context)):
77 return {"data": sentry.get_all(tenant_id=context.tenant_id)}
80@app.get('/{projectId}/integrations/sentry', tags=["integrations"])
81def get_sentry(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
82 return {"data": sentry.get(project_id=projectId)}
85@app.post('/{projectId}/integrations/sentry', tags=["integrations"])
86def add_edit_sentry(projectId: int, data: schemas.IntegrationSentrySchema = Body(...),
87 context: schemas.CurrentContext = Depends(OR_context)):
88 return {"data": sentry.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data)}
91@app.delete('/{projectId}/integrations/sentry', tags=["integrations"])
92def delete_sentry(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
93 return {"data": sentry.delete(tenant_id=context.tenant_id, project_id=projectId)}
96@app.get('/{projectId}/integrations/sentry/events/{eventId}', tags=["integrations"])
97def proxy_sentry(projectId: int, eventId: str, context: schemas.CurrentContext = Depends(OR_context)):
98 return {"data": sentry.proxy_get(tenant_id=context.tenant_id, project_id=projectId, event_id=eventId)}
101@app.get('/integrations/datadog', tags=["integrations"])
102def get_all_datadog(context: schemas.CurrentContext = Depends(OR_context)):
103 return {"data": datadog.get_all(tenant_id=context.tenant_id)}
106@app.get('/{projectId}/integrations/datadog', tags=["integrations"])
107def get_datadog(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
108 return {"data": datadog.get(project_id=projectId)}
111@app.post('/{projectId}/integrations/datadog', tags=["integrations"])
112def add_edit_datadog(projectId: int, data: schemas.IntegrationDatadogSchema = Body(...),
113 context: schemas.CurrentContext = Depends(OR_context)):
114 return {"data": datadog.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data)}
117@app.delete('/{projectId}/integrations/datadog', tags=["integrations"])
118def delete_datadog(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
119 return {"data": datadog.delete(tenant_id=context.tenant_id, project_id=projectId)}
122@app.get('/integrations/stackdriver', tags=["integrations"])
123def get_all_stackdriver(context: schemas.CurrentContext = Depends(OR_context)):
124 return {"data": stackdriver.get_all(tenant_id=context.tenant_id)}
127@app.get('/{projectId}/integrations/stackdriver', tags=["integrations"])
128def get_stackdriver(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
129 return {"data": stackdriver.get(project_id=projectId)}
132@app.post('/{projectId}/integrations/stackdriver', tags=["integrations"])
133def add_edit_stackdriver(projectId: int, data: schemas.IntegartionStackdriverSchema = Body(...),
134 context: schemas.CurrentContext = Depends(OR_context)):
135 return {"data": stackdriver.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data)}
138@app.delete('/{projectId}/integrations/stackdriver', tags=["integrations"])
139def delete_stackdriver(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
140 return {"data": stackdriver.delete(tenant_id=context.tenant_id, project_id=projectId)}
143@app.get('/integrations/newrelic', tags=["integrations"])
144def get_all_newrelic(context: schemas.CurrentContext = Depends(OR_context)):
145 return {"data": newrelic.get_all(tenant_id=context.tenant_id)}
148@app.get('/{projectId}/integrations/newrelic', tags=["integrations"])
149def get_newrelic(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
150 return {"data": newrelic.get(project_id=projectId)}
153@app.post('/{projectId}/integrations/newrelic', tags=["integrations"])
154def add_edit_newrelic(projectId: int, data: schemas.IntegrationNewrelicSchema = Body(...),
155 context: schemas.CurrentContext = Depends(OR_context)):
156 return {"data": newrelic.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data)}
159@app.delete('/{projectId}/integrations/newrelic', tags=["integrations"])
160def delete_newrelic(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
161 return {"data": newrelic.delete(tenant_id=context.tenant_id, project_id=projectId)}
164@app.get('/integrations/rollbar', tags=["integrations"])
165def get_all_rollbar(context: schemas.CurrentContext = Depends(OR_context)):
166 return {"data": rollbar.get_all(tenant_id=context.tenant_id)}
169@app.get('/{projectId}/integrations/rollbar', tags=["integrations"])
170def get_rollbar(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
171 return {"data": rollbar.get(project_id=projectId)}
174@app.post('/{projectId}/integrations/rollbar', tags=["integrations"])
175def add_edit_rollbar(projectId: int, data: schemas.IntegrationRollbarSchema = Body(...),
176 context: schemas.CurrentContext = Depends(OR_context)):
177 return {"data": rollbar.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data)}
180@app.delete('/{projectId}/integrations/rollbar', tags=["integrations"])
181def delete_datadog(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
182 return {"data": rollbar.delete(tenant_id=context.tenant_id, project_id=projectId)}
185@app.post('/integrations/bugsnag/list_projects', tags=["integrations"])
186def list_projects_bugsnag(data: schemas.IntegrationBugsnagBasicSchema = Body(...),
187 context: schemas.CurrentContext = Depends(OR_context)):
188 return {"data": bugsnag.list_projects(auth_token=data.authorization_token)}
191@app.get('/integrations/bugsnag', tags=["integrations"])
192def get_all_bugsnag(context: schemas.CurrentContext = Depends(OR_context)):
193 return {"data": bugsnag.get_all(tenant_id=context.tenant_id)}
196@app.get('/{projectId}/integrations/bugsnag', tags=["integrations"])
197def get_bugsnag(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
198 return {"data": bugsnag.get(project_id=projectId)}
201@app.post('/{projectId}/integrations/bugsnag', tags=["integrations"])
202def add_edit_bugsnag(projectId: int, data: schemas.IntegrationBugsnagSchema = Body(...),
203 context: schemas.CurrentContext = Depends(OR_context)):
204 return {"data": bugsnag.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data)}
207@app.delete('/{projectId}/integrations/bugsnag', tags=["integrations"])
208def delete_bugsnag(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
209 return {"data": bugsnag.delete(tenant_id=context.tenant_id, project_id=projectId)}
212@app.post('/integrations/cloudwatch/list_groups', tags=["integrations"])
213def list_groups_cloudwatch(data: schemas.IntegrationCloudwatchBasicSchema = Body(...),
214 context: schemas.CurrentContext = Depends(OR_context)):
215 return {"data": cloudwatch.list_log_groups(aws_access_key_id=data.awsAccessKeyId,
216 aws_secret_access_key=data.awsSecretAccessKey,
217 region=data.region)}
220@app.get('/integrations/cloudwatch', tags=["integrations"])
221def get_all_cloudwatch(context: schemas.CurrentContext = Depends(OR_context)):
222 return {"data": cloudwatch.get_all(tenant_id=context.tenant_id)}
225@app.get('/{projectId}/integrations/cloudwatch', tags=["integrations"])
226def get_cloudwatch(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
227 return {"data": cloudwatch.get(project_id=projectId)}
230@app.post('/{projectId}/integrations/cloudwatch', tags=["integrations"])
231def add_edit_cloudwatch(projectId: int, data: schemas.IntegrationCloudwatchSchema = Body(...),
232 context: schemas.CurrentContext = Depends(OR_context)):
233 return {"data": cloudwatch.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data)}
236@app.delete('/{projectId}/integrations/cloudwatch', tags=["integrations"])
237def delete_cloudwatch(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
238 return {"data": cloudwatch.delete(tenant_id=context.tenant_id, project_id=projectId)}
241@app.get('/integrations/elasticsearch', tags=["integrations"])
242def get_all_elasticsearch(context: schemas.CurrentContext = Depends(OR_context)):
243 return {"data": elasticsearch.get_all(tenant_id=context.tenant_id)}
246@app.get('/{projectId}/integrations/elasticsearch', tags=["integrations"])
247def get_elasticsearch(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
248 return {"data": elasticsearch.get(project_id=projectId)}
251@app.post('/integrations/elasticsearch/test', tags=["integrations"])
252def test_elasticsearch_connection(data: schemas.IntegrationElasticsearchTestSchema = Body(...),
253 context: schemas.CurrentContext = Depends(OR_context)):
254 return {"data": elasticsearch.ping(tenant_id=context.tenant_id, data=data)}
257@app.post('/{projectId}/integrations/elasticsearch', tags=["integrations"])
258def add_edit_elasticsearch(projectId: int, data: schemas.IntegrationElasticsearchSchema = Body(...),
259 context: schemas.CurrentContext = Depends(OR_context)):
260 return {
261 "data": elasticsearch.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data)}
264@app.delete('/{projectId}/integrations/elasticsearch', tags=["integrations"])
265def delete_elasticsearch(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
266 return {"data": elasticsearch.delete(tenant_id=context.tenant_id, project_id=projectId)}
269@app.get('/integrations/sumologic', tags=["integrations"])
270def get_all_sumologic(context: schemas.CurrentContext = Depends(OR_context)):
271 return {"data": sumologic.get_all(tenant_id=context.tenant_id)}
274@app.get('/{projectId}/integrations/sumologic', tags=["integrations"])
275def get_sumologic(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
276 return {"data": sumologic.get(project_id=projectId)}
279@app.post('/{projectId}/integrations/sumologic', tags=["integrations"])
280def add_edit_sumologic(projectId: int, data: schemas.IntegrationSumologicSchema = Body(...),
281 context: schemas.CurrentContext = Depends(OR_context)):
282 return {"data": sumologic.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data)}
285@app.delete('/{projectId}/integrations/sumologic', tags=["integrations"])
286def delete_sumologic(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
287 return {"data": sumologic.delete(tenant_id=context.tenant_id, project_id=projectId)}
290@app.get('/integrations/issues', tags=["integrations"])
291def get_status(context: schemas.CurrentContext = Depends(OR_context)):
292 error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id,
293 user_id=context.user_id)
294 if error is not None and integration is None: 294 ↛ 295line 294 didn't jump to line 295 because the condition on line 294 was never true
295 return {"data": {}}
296 return {"data": integration.get_obfuscated()}
299@app.get('/integrations/jira', tags=["integrations"])
300def get_status_jira(context: schemas.CurrentContext = Depends(OR_context)):
301 error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id,
302 user_id=context.user_id,
303 tool=jira_cloud.PROVIDER)
304 if error is not None and integration is None: 304 ↛ 305line 304 didn't jump to line 305 because the condition on line 304 was never true
305 return error
306 return {"data": integration.get_obfuscated()}
309@app.get('/integrations/github', tags=["integrations"])
310def get_status_github(context: schemas.CurrentContext = Depends(OR_context)):
311 error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id,
312 user_id=context.user_id,
313 tool=github.PROVIDER)
314 if error is not None and integration is None: 314 ↛ 315line 314 didn't jump to line 315 because the condition on line 314 was never true
315 return error
316 return {"data": integration.get_obfuscated()}
319@app.post('/integrations/jira', tags=["integrations"])
320def add_edit_jira_cloud(data: schemas.IssueTrackingJiraSchema = Body(...),
321 context: schemas.CurrentContext = Depends(OR_context)):
322 if not str(data.url).rstrip('/').endswith('atlassian.net'): 322 ↛ 324line 322 didn't jump to line 324 because the condition on line 322 was always true
323 return {"errors": ["url must be a valid JIRA URL (example.atlassian.net)"]}
324 error, integration = integrations_manager.get_integration(tool=jira_cloud.PROVIDER,
325 tenant_id=context.tenant_id,
326 user_id=context.user_id)
327 if error is not None and integration is None:
328 return error
329 return {"data": integration.add_edit(data=data)}
332@app.post('/integrations/github', tags=["integrations"])
333def add_edit_github(data: schemas.IssueTrackingGithubSchema = Body(...),
334 context: schemas.CurrentContext = Depends(OR_context)):
335 error, integration = integrations_manager.get_integration(tool=github.PROVIDER,
336 tenant_id=context.tenant_id,
337 user_id=context.user_id)
338 if error is not None: 338 ↛ 339line 338 didn't jump to line 339 because the condition on line 338 was never true
339 return error
340 return {"data": integration.add_edit(data=data)}
343@app.delete('/integrations/issues', tags=["integrations"])
344def delete_default_issue_tracking_tool(_=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
345 error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id,
346 user_id=context.user_id)
347 if error is not None and integration is None: 347 ↛ 349line 347 didn't jump to line 349 because the condition on line 347 was always true
348 return error
349 return {"data": integration.delete()}
352@app.delete('/integrations/jira', tags=["integrations"])
353def delete_jira_cloud(_=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
354 error, integration = integrations_manager.get_integration(tool=jira_cloud.PROVIDER,
355 tenant_id=context.tenant_id,
356 user_id=context.user_id,
357 for_delete=True)
358 if error is not None: 358 ↛ 359line 358 didn't jump to line 359 because the condition on line 358 was never true
359 return error
360 return {"data": integration.delete()}
363@app.delete('/integrations/github', tags=["integrations"])
364def delete_github(_=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
365 error, integration = integrations_manager.get_integration(tool=github.PROVIDER,
366 tenant_id=context.tenant_id,
367 user_id=context.user_id,
368 for_delete=True)
369 if error is not None: 369 ↛ 370line 369 didn't jump to line 370 because the condition on line 369 was never true
370 return error
371 return {"data": integration.delete()}
374@app.get('/integrations/issues/list_projects', tags=["integrations"])
375def get_all_issue_tracking_projects(context: schemas.CurrentContext = Depends(OR_context)):
376 error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id,
377 user_id=context.user_id)
378 if error is not None: 378 ↛ 379line 378 didn't jump to line 379 because the condition on line 378 was never true
379 return error
380 data = integration.issue_handler
381 if isinstance(data, dict) and "errors" in data: 381 ↛ 382line 381 didn't jump to line 382 because the condition on line 381 was never true
382 return data
383 data = data.get_projects()
384 if isinstance(data, dict) and "errors" in data:
385 return data
386 return {"data": data}
389@app.get('/integrations/issues/{integrationProjectId}', tags=["integrations"])
390def get_metadata(integrationProjectId: int, context: schemas.CurrentContext = Depends(OR_context)):
391 error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id,
392 user_id=context.user_id)
393 if error is not None: 393 ↛ 394line 393 didn't jump to line 394 because the condition on line 393 was never true
394 return error
395 data = integration
396 if isinstance(data, dict) and "errors" in data: 396 ↛ 397line 396 didn't jump to line 397 because the condition on line 396 was never true
397 return data
398 data = data.issue_handler.get_metas(integrationProjectId)
399 if isinstance(data, dict) and "errors" in data:
400 return data
401 return {"data": data}
404@app.get('/{projectId}/gdpr', tags=["projects", "gdpr"])
405def get_gdpr(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
406 return {"data": projects.get_gdpr(project_id=projectId)}
409@app.post('/{projectId}/gdpr', tags=["projects", "gdpr"])
410def edit_gdpr(projectId: int, data: schemas.GdprSchema = Body(...),
411 context: schemas.CurrentContext = Depends(OR_context)):
412 result = projects.edit_gdpr(project_id=projectId, gdpr=data)
413 if "errors" in result: 413 ↛ 414line 413 didn't jump to line 414 because the condition on line 413 was never true
414 return result
415 return {"data": result}
418@public_app.post('/password/reset-link', tags=["reset password"])
419def reset_password_handler(background_tasks: BackgroundTasks, data: schemas.ForgetPasswordPayloadSchema = Body(...)):
420 if len(data.email) < 5: 420 ↛ 421line 420 didn't jump to line 421 because the condition on line 420 was never true
421 return {"errors": ["please provide a valid email address"]}
422 return reset_password.reset(data=data, background_tasks=background_tasks)
425@app.get('/{projectId}/metadata', tags=["metadata"])
426def get_metadata(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
427 return {"data": metadata.get(project_id=projectId)}
430# @app.post('/{projectId}/metadata/list', tags=["metadata"])
431# def add_edit_delete_metadata(projectId: int, data: schemas.MetadataListSchema = Body(...),
432# context: schemas.CurrentContext = Depends(OR_context)):
433# return metadata.add_edit_delete(tenant_id=context.tenant_id, project_id=projectId, new_metas=data.list)
436@app.post('/{projectId}/metadata', tags=["metadata"])
437def add_metadata(projectId: int, data: schemas.MetadataSchema = Body(...),
438 context: schemas.CurrentContext = Depends(OR_context)):
439 return metadata.add(tenant_id=context.tenant_id, project_id=projectId, new_name=data.key)
442@app.post('/{projectId}/metadata/{index}', tags=["metadata"])
443def edit_metadata(projectId: int, index: int, data: schemas.MetadataSchema = Body(...),
444 context: schemas.CurrentContext = Depends(OR_context)):
445 return metadata.edit(tenant_id=context.tenant_id, project_id=projectId, index=index,
446 new_name=data.key)
449@app.delete('/{projectId}/metadata/{index}', tags=["metadata"])
450def delete_metadata(projectId: int, index: int, _=Body(None),
451 context: schemas.CurrentContext = Depends(OR_context)):
452 return metadata.delete(tenant_id=context.tenant_id, project_id=projectId, index=index)
455@app.get('/{projectId}/integration/sources', tags=["integrations"])
456def search_integrations(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
457 return log_tools.search(project_id=projectId)
460@app.get('/{projectId}/sample_rate', tags=["projects"])
461def get_capture_status(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
462 return {"data": projects.get_capture_status(project_id=projectId)}
465@app.post('/{projectId}/sample_rate', tags=["projects"])
466def update_capture_status(projectId: int, data: schemas.SampleRateSchema = Body(...),
467 context: schemas.CurrentContext = Depends(OR_context)):
468 return {"data": projects.update_capture_status(project_id=projectId, changes=data)}
471@app.post('/{projectId}/conditions', tags=["projects"])
472def update_conditions(projectId: int, data: schemas.ProjectSettings = Body(...),
473 context: schemas.CurrentContext = Depends(OR_context)):
474 return {"data": projects.update_conditions(project_id=projectId, changes=data)}
477@app.get('/{projectId}/conditions', tags=["projects"])
478def get_conditions(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
479 return {"data": projects.get_conditions(project_id=projectId)}
482@app.get('/announcements', tags=["announcements"])
483def get_all_announcements(context: schemas.CurrentContext = Depends(OR_context)):
484 return {"data": announcements.get_all(user_id=context.user_id)}
487@app.get('/announcements/view', tags=["announcements"])
488def get_all_announcements(context: schemas.CurrentContext = Depends(OR_context)):
489 return {"data": announcements.view(user_id=context.user_id)}
492@app.get('/show_banner', tags=["banner"])
493def errors_merge(context: schemas.CurrentContext = Depends(OR_context)):
494 return {"data": False}
497@app.post('/{projectId}/alerts', tags=["alerts"])
498def create_alert(projectId: int, data: schemas.AlertSchema = Body(...),
499 context: schemas.CurrentContext = Depends(OR_context)):
500 return alerts.create(project_id=projectId, data=data)
503@app.get('/{projectId}/alerts', tags=["alerts"])
504def get_all_alerts(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
505 return {"data": alerts.get_all(project_id=projectId)}
508@app.get('/{projectId}/alerts/triggers', tags=["alerts", "customMetrics"])
509def get_alerts_triggers(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
510 return {"data": alerts.get_predefined_values()
511 + custom_metrics.get_series_for_alert(project_id=projectId, user_id=context.user_id)}
514@app.get('/{projectId}/alerts/{alertId}', tags=["alerts"])
515def get_alert(projectId: int, alertId: int, context: schemas.CurrentContext = Depends(OR_context)):
516 return {"data": alerts.get(project_id=projectId, id=alertId)}
519@app.post('/{projectId}/alerts/{alertId}', tags=["alerts"])
520def update_alert(projectId: int, alertId: int, data: schemas.AlertSchema = Body(...),
521 context: schemas.CurrentContext = Depends(OR_context)):
522 return alerts.update(project_id=projectId, id=alertId, data=data)
525@app.delete('/{projectId}/alerts/{alertId}', tags=["alerts"])
526def delete_alert(projectId: int, alertId: int, _=Body(None),
527 context: schemas.CurrentContext = Depends(OR_context)):
528 return alerts.delete(project_id=projectId, alert_id=alertId)
531@app_apikey.put('/{projectKey}/sourcemaps/', tags=["sourcemaps"])
532@app_apikey.put('/{projectKey}/sourcemaps', tags=["sourcemaps"])
533def sign_sourcemap_for_upload(projectKey: str, data: schemas.SourcemapUploadPayloadSchema = Body(...),
534 context: schemas.CurrentContext = Depends(OR_context)):
535 return {"data": sourcemaps.presign_upload_urls(project_id=context.project.project_id, urls=data.urls)}
538@app.get('/config/weekly_report', tags=["weekly report config"])
539def get_weekly_report_config(context: schemas.CurrentContext = Depends(OR_context)):
540 return {"data": weekly_report.get_config(user_id=context.user_id)}
543@app.post('/config/weekly_report', tags=["weekly report config"])
544def edit_weekly_report_config(data: schemas.WeeklyReportConfigSchema = Body(...),
545 context: schemas.CurrentContext = Depends(OR_context)):
546 return {"data": weekly_report.edit_config(user_id=context.user_id, weekly_report=data.weekly_report)}
549@app.get('/{projectId}/assist/sessions', tags=["assist"])
550def get_sessions_live(projectId: int, userId: str = None, context: schemas.CurrentContext = Depends(OR_context)):
551 data = assist.get_live_sessions_ws_user_id(projectId, user_id=userId)
552 return {'data': data}
555@app.post('/{projectId}/assist/sessions', tags=["assist"])
556def sessions_live(projectId: int, data: schemas.LiveSessionsSearchPayloadSchema = Body(...),
557 context: schemas.CurrentContext = Depends(OR_context)):
558 data = assist.get_live_sessions_ws(projectId, body=data)
559 return {'data': data}
562@app.post('/{projectId}/mobile/{sessionId}/urls', tags=['mobile'])
563def mobile_signe(projectId: int, sessionId: int, data: schemas.MobileSignPayloadSchema = Body(...),
564 context: schemas.CurrentContext = Depends(OR_context)):
565 return {"data": mobile.sign_keys(project_id=projectId, session_id=sessionId, keys=data.keys)}
568@app.post('/projects', tags=['projects'], dependencies=[OR_role("owner", "admin")])
569def create_project(data: schemas.CreateProjectSchema = Body(...),
570 context: schemas.CurrentContext = Depends(OR_context)):
571 return projects.create(tenant_id=context.tenant_id, user_id=context.user_id, data=data)
574@app.get('/projects/{projectId}', tags=['projects'])
575def get_project(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
576 data = projects.get_project(tenant_id=context.tenant_id, project_id=projectId, include_last_session=True,
577 include_gdpr=True)
578 if data is None: 578 ↛ 579line 578 didn't jump to line 579 because the condition on line 578 was never true
579 return {"errors": ["project not found"]}
580 return {"data": data}
583@app.put('/projects/{projectId}', tags=['projects'], dependencies=[OR_role("owner", "admin")])
584def edit_project(projectId: int, data: schemas.CreateProjectSchema = Body(...),
585 context: schemas.CurrentContext = Depends(OR_context)):
586 return projects.edit(tenant_id=context.tenant_id, user_id=context.user_id, data=data, project_id=projectId)
589@app.delete('/projects/{projectId}', tags=['projects'], dependencies=[OR_role("owner", "admin")])
590def delete_project(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
591 return projects.delete(tenant_id=context.tenant_id, user_id=context.user_id, project_id=projectId)
594@app.post('/client/new_api_key', tags=['client'], dependencies=[OR_role("owner", "admin")])
595def generate_new_tenant_token(context: schemas.CurrentContext = Depends(OR_context)):
596 logger.info(f"tenant api_key rotated tenant_id={context.tenant_id} user_id={context.user_id}")
597 return {
598 'data': tenants.generate_new_api_key(context.tenant_id)
599 }
602@app.post('/users/modules', tags=['users'])
603def update_user_module(context: schemas.CurrentContext = Depends(OR_context),
604 data: schemas.ModuleStatus = Body(...)):
605 return {"data": users.update_user_module(context.user_id, data)}
608@app.get('/notifications', tags=['notifications'])
609def get_notifications(context: schemas.CurrentContext = Depends(OR_context)):
610 return {"data": notifications.get_all(tenant_id=context.tenant_id, user_id=context.user_id)}
613@app.get('/notifications/count', tags=['notifications'])
614def get_notifications_count(context: schemas.CurrentContext = Depends(OR_context)):
615 return {"data": notifications.get_all_count(tenant_id=context.tenant_id, user_id=context.user_id)}
618@app.get('/notifications/{notificationId}/view', tags=['notifications'])
619def view_notifications(notificationId: int, context: schemas.CurrentContext = Depends(OR_context)):
620 return {"data": notifications.view_notification(notification_ids=[notificationId], user_id=context.user_id)}
623@app.post('/notifications/view', tags=['notifications'])
624def batch_view_notifications(data: schemas.NotificationsViewSchema,
625 context: schemas.CurrentContext = Depends(OR_context)):
626 return {"data": notifications.view_notification(notification_ids=data.ids,
627 startTimestamp=data.startTimestamp,
628 endTimestamp=data.endTimestamp,
629 user_id=context.user_id,
630 tenant_id=context.tenant_id)}
633@app.get('/boarding', tags=['boarding'])
634def get_boarding_state(context: schemas.CurrentContext = Depends(OR_context)):
635 if config("LOCAL_DEV", cast=bool, default=False): 635 ↛ 636line 635 didn't jump to line 636 because the condition on line 635 was never true
636 return {"data": ""}
637 return {"data": boarding.get_state(tenant_id=context.tenant_id)}
640@app.get('/boarding/installing', tags=['boarding'])
641def get_boarding_state_installing(context: schemas.CurrentContext = Depends(OR_context)):
642 return {"data": boarding.get_state_installing(tenant_id=context.tenant_id)}
645@app.get('/boarding/identify-users', tags=["boarding"])
646def get_boarding_state_identify_users(context: schemas.CurrentContext = Depends(OR_context)):
647 return {"data": boarding.get_state_identify_users(tenant_id=context.tenant_id)}
650@app.get('/boarding/manage-users', tags=["boarding"])
651def get_boarding_state_manage_users(context: schemas.CurrentContext = Depends(OR_context)):
652 return {"data": boarding.get_state_manage_users(tenant_id=context.tenant_id)}
655@app.get('/boarding/integrations', tags=["boarding"])
656def get_boarding_state_integrations(context: schemas.CurrentContext = Depends(OR_context)):
657 return {"data": boarding.get_state_integrations(tenant_id=context.tenant_id)}
660@app.get('/integrations/slack/channels', tags=["integrations"])
661def get_slack_channels(context: schemas.CurrentContext = Depends(OR_context)):
662 return {"data": webhook.get_by_type(tenant_id=context.tenant_id, webhook_type=schemas.WebhookType.SLACK)}
665@app.get('/integrations/slack/{integrationId}', tags=["integrations"])
666def get_slack_webhook(integrationId: int, context: schemas.CurrentContext = Depends(OR_context)):
667 return {"data": Slack.get_integration(tenant_id=context.tenant_id, id=integrationId)}
670@app.delete('/integrations/slack/{integrationId}', tags=["integrations"])
671def delete_slack_integration(integrationId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
672 return webhook.delete(tenant_id=context.tenant_id, webhook_id=integrationId)
675@app.put('/webhooks', tags=["webhooks"])
676def add_edit_webhook(data: schemas.WebhookSchema = Body(...),
677 context: schemas.CurrentContext = Depends(OR_context)):
678 return {"data": webhook.add_edit(tenant_id=context.tenant_id, data=data, replace_none=True)}
681@app.get('/webhooks', tags=["webhooks"])
682def get_webhooks(context: schemas.CurrentContext = Depends(OR_context)):
683 return {"data": webhook.get_by_tenant(tenant_id=context.tenant_id, replace_none=True)}
686@app.delete('/webhooks/{webhookId}', tags=["webhooks"])
687def delete_webhook(webhookId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
688 return webhook.delete(tenant_id=context.tenant_id, webhook_id=webhookId)
691@app.get('/client/members', tags=["client"], dependencies=[OR_role("owner", "admin")])
692def get_members(context: schemas.CurrentContext = Depends(OR_context)):
693 return {"data": users.get_members(tenant_id=context.tenant_id)}
696@app.get('/client/members/{memberId}/reset', tags=["client"], dependencies=[OR_role("owner", "admin")])
697def reset_reinvite_member(memberId: int, context: schemas.CurrentContext = Depends(OR_context)):
698 return users.reset_member(tenant_id=context.tenant_id, editor_id=context.user_id, user_id_to_update=memberId)
701@app.delete('/client/members/{memberId}', tags=["client"], dependencies=[OR_role("owner", "admin")])
702def delete_member(memberId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
703 return users.delete_member(tenant_id=context.tenant_id, user_id=context.user_id, id_to_delete=memberId)
706@app.put('/client/members/{memberId}/owner', tags=["client"], dependencies=[OR_role("owner")])
707def transfer_ownership(memberId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
708 return users.transfer_ownership(tenant_id=context.tenant_id, user_id=context.user_id, new_owner_id=memberId)
711@app.get('/account/new_api_key', tags=["account"], dependencies=[OR_role("owner", "admin")])
712def generate_new_user_token(context: schemas.CurrentContext = Depends(OR_context)):
713 return {"data": users.generate_new_api_key(user_id=context.user_id)}
716@app.get('/limits', tags=['accounts'])
717def get_limits(context: schemas.CurrentContext = Depends(OR_context)):
718 return {
719 'data': {
720 "teamMember": -1,
721 "projects": -1,
722 }
723 }
726@app.get('/integrations/msteams/channels', tags=["integrations"])
727def get_msteams_channels(context: schemas.CurrentContext = Depends(OR_context)):
728 return {"data": webhook.get_by_type(tenant_id=context.tenant_id, webhook_type=schemas.WebhookType.MSTEAMS)}
731@app.post('/integrations/msteams', tags=['integrations'])
732def add_msteams_integration(data: schemas.AddCollaborationSchema,
733 context: schemas.CurrentContext = Depends(OR_context)):
734 n = MSTeams.add(tenant_id=context.tenant_id, data=data)
735 if n is None: 735 ↛ 740line 735 didn't jump to line 740 because the condition on line 735 was always true
736 return {
737 "errors": [
738 "We couldn't send you a test message on your Microsoft Teams channel. Please verify your webhook url."]
739 }
740 return {"data": n}
743@app.post('/integrations/msteams/{webhookId}', tags=['integrations'])
744def edit_msteams_integration(webhookId: int, data: schemas.EditCollaborationSchema = Body(...),
745 context: schemas.CurrentContext = Depends(OR_context)):
746 if len(data.url.unicode_string()) > 0: 746 ↛ 756line 746 didn't jump to line 756 because the condition on line 746 was always true
747 old = MSTeams.get_integration(tenant_id=context.tenant_id, id=webhookId)
748 if not old:
749 return {"errors": ["MsTeams integration not found."]}
750 if old["endpoint"] != data.url.unicode_string():
751 if not MSTeams.say_hello(data.url.unicode_string()):
752 return {
753 "errors": ["We couldn't send you a test message on your Microsoft Teams channel. "
754 "Please verify your webhook url."]
755 }
756 return {"data": webhook.update(tenant_id=context.tenant_id, webhook_id=webhookId,
757 changes={"name": data.name, "endpoint": data.url.unicode_string()})}
760@app.delete('/integrations/msteams/{webhookId}', tags=["integrations"])
761def delete_msteams_integration(webhookId: int, _=Body(None),
762 context: schemas.CurrentContext = Depends(OR_context)):
763 return webhook.delete(tenant_id=context.tenant_id, webhook_id=webhookId)
766@public_app.get('/', tags=["health"])
767def health_check():
768 return {}
771@app.post('/{projectId}/tags', tags=["tags"])
772def tags_create(projectId: int, data: schemas.TagCreate = Body(),
773 context: schemas.CurrentContext = Depends(OR_context)):
774 data = tags.create_tag(project_id=projectId, data=data)
775 return {'data': data}
778@app.put('/{projectId}/tags/{tagId}', tags=["tags"])
779def tags_update(projectId: int, tagId: int, data: schemas.TagUpdate = Body(),
780 context: schemas.CurrentContext = Depends(OR_context)):
781 data = tags.update_tag(project_id=projectId, tag_id=tagId, data=data)
782 return {'data': data}
785@app.get('/{projectId}/tags', tags=["tags"])
786def tags_list(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
787 data = tags.list_tags(project_id=projectId)
788 return {'data': data}
791@app.delete('/{projectId}/tags/{tagId}', tags=["tags"])
792def tags_delete(projectId: int, tagId: int, context: schemas.CurrentContext = Depends(OR_context)):
793 data = tags.delete_tag(projectId, tag_id=tagId)
794 return {'data': data}