Coverage for routers/core.py: 84%

455 statements  

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

1import logging 

2from typing import Union, Optional 

3 

4from decouple import config 

5from fastapi import Depends, Body, BackgroundTasks 

6 

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 

21 

22public_app, app, app_apikey = get_routers() 

23 

24logger = logging.getLogger(__name__) 

25 

26 

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) 

38 

39 return {"data": [], "errors": ["not available for global search"]} 

40 

41 

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} 

48 

49 

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 

57 

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} 

73 

74 

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)} 

78 

79 

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)} 

83 

84 

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)} 

89 

90 

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)} 

94 

95 

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)} 

99 

100 

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)} 

104 

105 

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)} 

109 

110 

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)} 

115 

116 

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)} 

120 

121 

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)} 

125 

126 

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)} 

130 

131 

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)} 

136 

137 

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)} 

141 

142 

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)} 

146 

147 

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)} 

151 

152 

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)} 

157 

158 

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)} 

162 

163 

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)} 

167 

168 

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)} 

172 

173 

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)} 

178 

179 

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)} 

183 

184 

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)} 

189 

190 

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)} 

194 

195 

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)} 

199 

200 

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)} 

205 

206 

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)} 

210 

211 

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)} 

218 

219 

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)} 

223 

224 

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)} 

228 

229 

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)} 

234 

235 

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)} 

239 

240 

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)} 

244 

245 

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)} 

249 

250 

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)} 

255 

256 

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)} 

262 

263 

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)} 

267 

268 

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)} 

272 

273 

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)} 

277 

278 

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)} 

283 

284 

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)} 

288 

289 

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()} 

297 

298 

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()} 

307 

308 

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()} 

317 

318 

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)} 

330 

331 

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)} 

341 

342 

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()} 

350 

351 

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()} 

361 

362 

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()} 

372 

373 

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} 

387 

388 

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} 

402 

403 

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)} 

407 

408 

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} 

416 

417 

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) 

423 

424 

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)} 

428 

429 

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) 

434 

435 

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) 

440 

441 

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) 

447 

448 

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) 

453 

454 

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) 

458 

459 

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)} 

463 

464 

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)} 

469 

470 

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)} 

475 

476 

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)} 

480 

481 

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)} 

485 

486 

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)} 

490 

491 

492@app.get('/show_banner', tags=["banner"]) 

493def errors_merge(context: schemas.CurrentContext = Depends(OR_context)): 

494 return {"data": False} 

495 

496 

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) 

501 

502 

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)} 

506 

507 

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)} 

512 

513 

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)} 

517 

518 

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) 

523 

524 

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) 

529 

530 

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)} 

536 

537 

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)} 

541 

542 

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)} 

547 

548 

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} 

553 

554 

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} 

560 

561 

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)} 

566 

567 

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) 

572 

573 

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} 

581 

582 

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) 

587 

588 

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) 

592 

593 

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 } 

600 

601 

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)} 

606 

607 

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)} 

611 

612 

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)} 

616 

617 

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)} 

621 

622 

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)} 

631 

632 

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)} 

638 

639 

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)} 

643 

644 

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)} 

648 

649 

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)} 

653 

654 

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)} 

658 

659 

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)} 

663 

664 

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)} 

668 

669 

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) 

673 

674 

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)} 

679 

680 

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)} 

684 

685 

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) 

689 

690 

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)} 

694 

695 

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) 

699 

700 

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) 

704 

705 

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) 

709 

710 

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)} 

714 

715 

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 } 

724 

725 

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)} 

729 

730 

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} 

741 

742 

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()})} 

758 

759 

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) 

764 

765 

766@public_app.get('/', tags=["health"]) 

767def health_check(): 

768 return {} 

769 

770 

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} 

776 

777 

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} 

783 

784 

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} 

789 

790 

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}