argus-alm 0.14.1__py3-none-any.whl → 0.15.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. argus/_version.py +21 -0
  2. argus/backend/.gitkeep +0 -0
  3. argus/backend/__init__.py +0 -0
  4. argus/backend/cli.py +57 -0
  5. argus/backend/controller/__init__.py +0 -0
  6. argus/backend/controller/admin.py +20 -0
  7. argus/backend/controller/admin_api.py +355 -0
  8. argus/backend/controller/api.py +589 -0
  9. argus/backend/controller/auth.py +67 -0
  10. argus/backend/controller/client_api.py +109 -0
  11. argus/backend/controller/main.py +316 -0
  12. argus/backend/controller/notification_api.py +72 -0
  13. argus/backend/controller/notifications.py +13 -0
  14. argus/backend/controller/planner_api.py +194 -0
  15. argus/backend/controller/team.py +129 -0
  16. argus/backend/controller/team_ui.py +19 -0
  17. argus/backend/controller/testrun_api.py +513 -0
  18. argus/backend/controller/view_api.py +188 -0
  19. argus/backend/controller/views_widgets/__init__.py +0 -0
  20. argus/backend/controller/views_widgets/graphed_stats.py +54 -0
  21. argus/backend/controller/views_widgets/graphs.py +68 -0
  22. argus/backend/controller/views_widgets/highlights.py +135 -0
  23. argus/backend/controller/views_widgets/nemesis_stats.py +26 -0
  24. argus/backend/controller/views_widgets/summary.py +43 -0
  25. argus/backend/db.py +98 -0
  26. argus/backend/error_handlers.py +41 -0
  27. argus/backend/events/event_processors.py +34 -0
  28. argus/backend/models/__init__.py +0 -0
  29. argus/backend/models/argus_ai.py +24 -0
  30. argus/backend/models/github_issue.py +60 -0
  31. argus/backend/models/plan.py +24 -0
  32. argus/backend/models/result.py +187 -0
  33. argus/backend/models/runtime_store.py +58 -0
  34. argus/backend/models/view_widgets.py +25 -0
  35. argus/backend/models/web.py +403 -0
  36. argus/backend/plugins/__init__.py +0 -0
  37. argus/backend/plugins/core.py +248 -0
  38. argus/backend/plugins/driver_matrix_tests/controller.py +66 -0
  39. argus/backend/plugins/driver_matrix_tests/model.py +429 -0
  40. argus/backend/plugins/driver_matrix_tests/plugin.py +21 -0
  41. argus/backend/plugins/driver_matrix_tests/raw_types.py +62 -0
  42. argus/backend/plugins/driver_matrix_tests/service.py +61 -0
  43. argus/backend/plugins/driver_matrix_tests/udt.py +42 -0
  44. argus/backend/plugins/generic/model.py +86 -0
  45. argus/backend/plugins/generic/plugin.py +15 -0
  46. argus/backend/plugins/generic/types.py +14 -0
  47. argus/backend/plugins/loader.py +39 -0
  48. argus/backend/plugins/sct/controller.py +224 -0
  49. argus/backend/plugins/sct/plugin.py +37 -0
  50. argus/backend/plugins/sct/resource_setup.py +177 -0
  51. argus/backend/plugins/sct/service.py +682 -0
  52. argus/backend/plugins/sct/testrun.py +288 -0
  53. argus/backend/plugins/sct/udt.py +100 -0
  54. argus/backend/plugins/sirenada/model.py +118 -0
  55. argus/backend/plugins/sirenada/plugin.py +16 -0
  56. argus/backend/service/admin.py +26 -0
  57. argus/backend/service/argus_service.py +696 -0
  58. argus/backend/service/build_system_monitor.py +185 -0
  59. argus/backend/service/client_service.py +127 -0
  60. argus/backend/service/event_service.py +18 -0
  61. argus/backend/service/github_service.py +233 -0
  62. argus/backend/service/jenkins_service.py +269 -0
  63. argus/backend/service/notification_manager.py +159 -0
  64. argus/backend/service/planner_service.py +608 -0
  65. argus/backend/service/release_manager.py +229 -0
  66. argus/backend/service/results_service.py +690 -0
  67. argus/backend/service/stats.py +610 -0
  68. argus/backend/service/team_manager_service.py +82 -0
  69. argus/backend/service/test_lookup.py +172 -0
  70. argus/backend/service/testrun.py +489 -0
  71. argus/backend/service/user.py +308 -0
  72. argus/backend/service/views.py +219 -0
  73. argus/backend/service/views_widgets/__init__.py +0 -0
  74. argus/backend/service/views_widgets/graphed_stats.py +180 -0
  75. argus/backend/service/views_widgets/highlights.py +374 -0
  76. argus/backend/service/views_widgets/nemesis_stats.py +34 -0
  77. argus/backend/template_filters.py +27 -0
  78. argus/backend/tests/__init__.py +0 -0
  79. argus/backend/tests/client_service/__init__.py +0 -0
  80. argus/backend/tests/client_service/test_submit_results.py +79 -0
  81. argus/backend/tests/conftest.py +180 -0
  82. argus/backend/tests/results_service/__init__.py +0 -0
  83. argus/backend/tests/results_service/test_best_results.py +178 -0
  84. argus/backend/tests/results_service/test_cell.py +65 -0
  85. argus/backend/tests/results_service/test_chartjs_additional_functions.py +259 -0
  86. argus/backend/tests/results_service/test_create_chartjs.py +220 -0
  87. argus/backend/tests/results_service/test_result_metadata.py +100 -0
  88. argus/backend/tests/results_service/test_results_service.py +203 -0
  89. argus/backend/tests/results_service/test_validation_rules.py +213 -0
  90. argus/backend/tests/view_widgets/__init__.py +0 -0
  91. argus/backend/tests/view_widgets/test_highlights_api.py +532 -0
  92. argus/backend/util/common.py +65 -0
  93. argus/backend/util/config.py +38 -0
  94. argus/backend/util/encoders.py +56 -0
  95. argus/backend/util/logsetup.py +80 -0
  96. argus/backend/util/module_loaders.py +30 -0
  97. argus/backend/util/send_email.py +91 -0
  98. argus/client/base.py +1 -3
  99. argus/client/driver_matrix_tests/cli.py +19 -10
  100. argus/client/generic/cli.py +4 -2
  101. argus/client/generic/client.py +1 -0
  102. argus/client/generic_result.py +48 -9
  103. argus/client/sct/client.py +1 -3
  104. argus/client/sirenada/client.py +4 -1
  105. argus/client/tests/__init__.py +0 -0
  106. argus/client/tests/conftest.py +19 -0
  107. argus/client/tests/test_package.py +45 -0
  108. argus/client/tests/test_results.py +224 -0
  109. argus/common/sct_types.py +3 -0
  110. argus/common/sirenada_types.py +1 -1
  111. {argus_alm-0.14.1.dist-info → argus_alm-0.15.1.dist-info}/METADATA +43 -19
  112. argus_alm-0.15.1.dist-info/RECORD +122 -0
  113. {argus_alm-0.14.1.dist-info → argus_alm-0.15.1.dist-info}/WHEEL +2 -1
  114. argus_alm-0.15.1.dist-info/entry_points.txt +3 -0
  115. argus_alm-0.15.1.dist-info/top_level.txt +1 -0
  116. argus_alm-0.14.1.dist-info/RECORD +0 -20
  117. argus_alm-0.14.1.dist-info/entry_points.txt +0 -4
  118. {argus_alm-0.14.1.dist-info → argus_alm-0.15.1.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,589 @@
1
+ import logging
2
+ from datetime import datetime, timezone
3
+ from uuid import UUID
4
+ import requests
5
+ from flask import (
6
+ Blueprint,
7
+ g,
8
+ redirect,
9
+ request, Response
10
+ )
11
+ from flask.json import jsonify
12
+ from argus.backend.error_handlers import handle_api_exception
13
+ from argus.backend.controller.notification_api import bp as notifications_bp
14
+ from argus.backend.controller.client_api import bp as client_bp
15
+ from argus.backend.controller.testrun_api import bp as testrun_bp
16
+ from argus.backend.controller.team import bp as team_bp
17
+ from argus.backend.controller.view_api import bp as view_bp
18
+ from argus.backend.controller.planner_api import bp as planner_bp
19
+ from argus.backend.service.argus_service import ArgusService, ScheduleUpdateRequest
20
+ from argus.backend.service.results_service import ResultsService
21
+ from argus.backend.service.testrun import TestRunService
22
+ from argus.backend.service.user import UserService, api_login_required
23
+ from argus.backend.service.stats import ReleaseStatsCollector
24
+ from argus.backend.models.web import ArgusRelease, ArgusGroup, ArgusTest, User, UserOauthToken
25
+ from argus.backend.util.common import get_payload
26
+
27
+ bp = Blueprint('api', __name__, url_prefix='/api/v1')
28
+ bp.register_blueprint(notifications_bp)
29
+ bp.register_blueprint(client_bp)
30
+ bp.register_blueprint(testrun_bp)
31
+ bp.register_blueprint(team_bp)
32
+ bp.register_blueprint(view_bp)
33
+ bp.register_blueprint(planner_bp)
34
+ bp.register_error_handler(Exception, handle_api_exception)
35
+ LOGGER = logging.getLogger(__name__)
36
+
37
+
38
+ @bp.route("/version")
39
+ def app_version():
40
+ service = ArgusService()
41
+ argus_version = service.get_version()
42
+ return jsonify({
43
+ "status": "ok",
44
+ "response": {
45
+ "commit_id": argus_version
46
+ }
47
+ })
48
+
49
+
50
+ @bp.route("/profile/github/token")
51
+ @api_login_required
52
+ def get_github_oauth_token():
53
+ user_tokens = UserOauthToken.filter(user_id=g.user.id).all()
54
+ token = None
55
+ for tok in user_tokens:
56
+ if tok.kind == "github":
57
+ token = tok.token
58
+ break
59
+ if not token:
60
+ raise Exception("Github token not found")
61
+
62
+ res = jsonify({
63
+ "status": "ok",
64
+ "response": token
65
+ })
66
+ res.cache_control.max_age = 300
67
+
68
+ return res
69
+
70
+
71
+ @bp.route("/releases")
72
+ @api_login_required
73
+ def releases():
74
+ service = ArgusService()
75
+ force_all = request.args.get("all", False)
76
+ all_releases = service.get_releases()
77
+ response = jsonify({
78
+ "status": "ok",
79
+ "response": [dict(d.items()) for d in all_releases if d.enabled or force_all]
80
+ })
81
+
82
+ response.cache_control.max_age = 60
83
+ return response
84
+
85
+
86
+ @bp.route("/release/activity", methods=["GET"])
87
+ @api_login_required
88
+ def release_activity():
89
+ release_name = request.args.get("releaseName")
90
+ if not release_name:
91
+ raise Exception("Release name not specified in the request")
92
+ service = ArgusService()
93
+ activity_data = service.fetch_release_activity(release_name)
94
+
95
+ return jsonify({
96
+ "status": "ok",
97
+ "response": activity_data
98
+ })
99
+
100
+
101
+ @bp.route("/release/planner/data", methods=["GET"])
102
+ @api_login_required
103
+ def release_planner_data():
104
+
105
+ release_id = request.args.get("releaseId")
106
+ if not release_id:
107
+ raise Exception("Release Id not specified")
108
+ service = ArgusService()
109
+ planner_data = service.get_planner_data(release_id)
110
+ return jsonify({
111
+ "status": "ok",
112
+ "response": planner_data
113
+ })
114
+
115
+
116
+ @bp.route("/release/<string:release_id>/versions")
117
+ @api_login_required
118
+ def release_versions(release_id: str):
119
+ release_id = UUID(release_id)
120
+ service = ArgusService()
121
+ distinct_versions = service.get_distinct_release_versions(release_id=release_id)
122
+
123
+ return jsonify({
124
+ "status": "ok",
125
+ "response": distinct_versions
126
+ })
127
+
128
+
129
+ @bp.route("/release/planner/comment/get/test")
130
+ def get_planner_comment_by_test():
131
+ test_id = request.args.get("id")
132
+ if not test_id:
133
+ raise Exception("TestId was not specified")
134
+ service = ArgusService()
135
+ planner_comments_by_test = service.get_planner_comment_by_test(UUID(test_id))
136
+
137
+ response = jsonify({
138
+ "status": "ok",
139
+ "response": planner_comments_by_test
140
+ })
141
+ response.cache_control.max_age = 60
142
+ return response
143
+
144
+
145
+ @bp.route("/release/schedules/comment/update", methods=["POST"])
146
+ @api_login_required
147
+ def release_schedules_comment_update():
148
+ if not request.is_json:
149
+ raise Exception(
150
+ "Content-Type mismatch, expected application/json, got:", request.content_type)
151
+ request_payload = request.get_json()
152
+ service = ArgusService()
153
+ comment_update_result = service.update_schedule_comment(request_payload)
154
+
155
+ return jsonify({
156
+ "status": "ok",
157
+ "response": comment_update_result
158
+ })
159
+
160
+
161
+ @bp.route("/release/schedules", methods=["GET"])
162
+ @api_login_required
163
+ def release_schedules():
164
+ release = request.args.get("releaseId")
165
+ if not release:
166
+ raise Exception("No releaseId provided")
167
+ service = ArgusService()
168
+ release_schedules_data = service.get_schedules_for_release(release)
169
+
170
+ return jsonify({
171
+ "status": "ok",
172
+ "response": release_schedules_data
173
+ })
174
+
175
+
176
+ @bp.route("/release/schedules/assignee/update", methods=["POST"])
177
+ @api_login_required
178
+ def release_schedules_assignee_update():
179
+ if not request.is_json:
180
+ raise Exception(
181
+ "Content-Type mismatch, expected application/json, got:", request.content_type)
182
+ request_payload = request.get_json()
183
+ service = ArgusService()
184
+ assignee_update_status = service.update_schedule_assignees(request_payload)
185
+
186
+ return jsonify({
187
+ "status": "ok",
188
+ "response": assignee_update_status
189
+ })
190
+
191
+
192
+ @bp.route("/release/assignees/groups", methods=["GET"])
193
+ @api_login_required
194
+ def group_assignees():
195
+ release_id = request.args.get("releaseId")
196
+ version = request.args.get("version")
197
+ plan_id = request.args.get("planId")
198
+ if not release_id:
199
+ raise Exception("Missing releaseId")
200
+ service = ArgusService()
201
+ group_assignees_list = service.get_groups_assignees(release_id, version, plan_id)
202
+
203
+ return jsonify({
204
+ "status": "ok",
205
+ "response": group_assignees_list
206
+ })
207
+
208
+
209
+ @bp.route("/release/assignees/tests", methods=["GET"])
210
+ @api_login_required
211
+ def tests_assignees():
212
+ group_id = request.args.get("groupId")
213
+ version = request.args.get("version")
214
+ plan_id = request.args.get("planId")
215
+ if not group_id:
216
+ raise Exception("Missing groupId")
217
+ service = ArgusService()
218
+ tests_assignees_list = service.get_tests_assignees(group_id, version, plan_id)
219
+
220
+ return jsonify({
221
+ "status": "ok",
222
+ "response": tests_assignees_list
223
+ })
224
+
225
+
226
+ @bp.route("/release/schedules/submit", methods=["POST"])
227
+ @api_login_required
228
+ def release_schedules_submit():
229
+ if not request.is_json:
230
+ raise Exception(
231
+ "Content-Type mismatch, expected application/json, got:", request.content_type)
232
+ payload = request.get_json()
233
+ service = ArgusService()
234
+ schedule_submit_result = service.submit_new_schedule(
235
+ release=payload["releaseId"],
236
+ start_time=payload["start"],
237
+ end_time=payload["end"],
238
+ tests=payload["tests"],
239
+ groups=payload["groups"],
240
+ assignees=payload["assignees"],
241
+ tag=payload["tag"],
242
+ comments=payload.get("comments"),
243
+ group_ids=payload.get("groupIds"),
244
+ )
245
+
246
+ return jsonify({
247
+ "status": "ok",
248
+ "response": schedule_submit_result
249
+ })
250
+
251
+
252
+ @bp.route("/release/schedules/delete", methods=["POST"])
253
+ @api_login_required
254
+ def release_schedules_delete():
255
+ if not request.is_json:
256
+ raise Exception(
257
+ "Content-Type mismatch, expected application/json, got:", request.content_type)
258
+ request_payload = request.get_json()
259
+ service = ArgusService()
260
+ schedule_delete_result = service.delete_schedule(request_payload)
261
+
262
+ return jsonify({
263
+ "status": "ok",
264
+ "response": schedule_delete_result
265
+ })
266
+
267
+
268
+ @bp.route("/release/schedules/update", methods=["POST"])
269
+ @api_login_required
270
+ def release_schedule_update():
271
+ payload = get_payload(request)
272
+ req = ScheduleUpdateRequest(**payload)
273
+ service = ArgusService()
274
+ update_result = service.update_schedule(
275
+ release_id=req.release_id,
276
+ schedule_id=req.schedule_id,
277
+ old_tests=req.old_tests,
278
+ new_tests=req.new_tests,
279
+ comments=req.comments,
280
+ assignee=req.assignee
281
+ )
282
+
283
+ return jsonify({
284
+ "status": "ok",
285
+ "response": update_result
286
+ })
287
+
288
+
289
+ @bp.route("/groups", methods=["GET"])
290
+ @api_login_required
291
+ def argus_groups():
292
+ release_id = request.args.get("releaseId")
293
+ if not release_id:
294
+ raise Exception("No releaseId provided")
295
+
296
+ force_all = request.args.get("all", False)
297
+ service = ArgusService()
298
+ groups = service.get_groups(UUID(release_id))
299
+ result_groups = [dict(g.items()) for g in groups if g.enabled or force_all]
300
+
301
+ response = jsonify({
302
+ "status": "ok",
303
+ "response": result_groups
304
+ })
305
+ response.cache_control.max_age = 60
306
+ return response
307
+
308
+
309
+ @bp.route("/tests", methods=["GET"])
310
+ @api_login_required
311
+ def argus_tests():
312
+ group_id = request.args.get("groupId")
313
+ if not group_id:
314
+ raise Exception("No groupId provided")
315
+ force_all = request.args.get("all", False)
316
+ service = ArgusService()
317
+ tests = service.get_tests(group_id=group_id)
318
+ result_tests = [dict(t.items()) for t in tests if t.enabled or force_all]
319
+
320
+ response = jsonify({
321
+ "status": "ok",
322
+ "response": result_tests
323
+ })
324
+ response.cache_control.max_age = 60
325
+ return response
326
+
327
+
328
+ @bp.route("/release/<string:release_id>/details", methods=["GET"])
329
+ @api_login_required
330
+ def get_release_details(release_id: str):
331
+ release = ArgusRelease.get(id=UUID(release_id))
332
+ response = jsonify({
333
+ "status": "ok",
334
+ "response": release,
335
+ })
336
+ response.cache_control.max_age = 60
337
+ return response
338
+
339
+
340
+ @bp.route("/group/<string:group_id>/details", methods=["GET"])
341
+ @api_login_required
342
+ def get_group_details(group_id: str):
343
+ group = ArgusGroup.get(id=UUID(group_id))
344
+ response = jsonify({
345
+ "status": "ok",
346
+ "response": group,
347
+ })
348
+ response.cache_control.max_age = 60
349
+ return response
350
+
351
+
352
+ @bp.route("/test/<string:test_id>/details", methods=["GET"])
353
+ @api_login_required
354
+ def get_test_details(test_id: str):
355
+ test = ArgusTest.get(id=UUID(test_id))
356
+ response = jsonify({
357
+ "status": "ok",
358
+ "response": test
359
+ })
360
+ response.cache_control.max_age = 60
361
+ return response
362
+
363
+
364
+ @bp.route("/test/<string:test_id>/set_plugin", methods=["POST"])
365
+ @api_login_required
366
+ def set_test_plugin(test_id: str):
367
+ payload = get_payload(request)
368
+
369
+ current_user: User = g.user
370
+ test: ArgusTest = ArgusTest.get(id=UUID(test_id))
371
+ test.plugin_name = payload["plugin_name"]
372
+ test.save()
373
+
374
+ return {
375
+ "status": "ok",
376
+ "response": test
377
+ }
378
+
379
+
380
+ @bp.route("/test-info", methods=["GET"])
381
+ @api_login_required
382
+ def test_info():
383
+ test_id = request.args.get("testId")
384
+ if not test_id:
385
+ raise Exception("No testId provided")
386
+ service = ArgusService()
387
+ info = service.get_test_info(test_id=UUID(test_id))
388
+
389
+ return {
390
+ "status": "ok",
391
+ "response": info
392
+ }
393
+
394
+
395
+ @bp.route("/test-results", methods=["GET", "HEAD"])
396
+ @api_login_required
397
+ def test_results():
398
+ test_id = request.args.get("testId")
399
+ start_date_str = request.args.get("startDate")
400
+ end_date_str = request.args.get("endDate")
401
+ table_names = request.args.getlist("tableNames[]")
402
+
403
+ if not test_id:
404
+ raise Exception("No testId provided")
405
+
406
+ start_date = datetime.fromisoformat(start_date_str).astimezone(timezone.utc) if start_date_str else None
407
+ end_date = datetime.fromisoformat(end_date_str).astimezone(timezone.utc) if end_date_str else None
408
+
409
+ service = ResultsService()
410
+ if request.method == 'HEAD':
411
+ exists = service.is_results_exist(test_id=UUID(test_id))
412
+ return Response(status=200 if exists else 404)
413
+
414
+ graphs, ticks, releases_filters = service.get_test_graphs(test_id=UUID(
415
+ test_id), start_date=start_date, end_date=end_date, table_names=table_names)
416
+ graph_views = service.get_argus_graph_views(test_id=UUID(test_id))
417
+
418
+ return {
419
+ "status": "ok",
420
+ "response": {"graphs": graphs, "ticks": ticks, "releases_filters": releases_filters, "graph_views": graph_views}
421
+ }
422
+
423
+
424
+ @bp.route("/create-graph-view", methods=["POST"])
425
+ @api_login_required
426
+ def create_graph_view():
427
+ payload = get_payload(request)
428
+ service = ResultsService()
429
+ test_id = payload["testId"]
430
+ name = payload["name"]
431
+ description = payload["description"]
432
+ graph_view = service.create_argus_graph_view(test_id=UUID(test_id), name=name, description=description)
433
+ return {
434
+ "status": "ok",
435
+ "response": graph_view
436
+ }
437
+
438
+
439
+ @bp.route("/update-graph-view", methods=["POST"])
440
+ @api_login_required
441
+ def update_graph_view():
442
+ payload = get_payload(request)
443
+ service = ResultsService()
444
+ test_id = payload["testId"]
445
+ id = payload["id"]
446
+ name = payload["name"]
447
+ description = payload["description"]
448
+ graphs = payload["graphs"]
449
+ graph_view = service.update_argus_graph_view(test_id=UUID(test_id), view_id=UUID(id), name=name, description=description,
450
+ graphs=graphs)
451
+ return {
452
+ "status": "ok",
453
+ "response": graph_view
454
+ }
455
+
456
+
457
+ @bp.route("/test_run/comment/get", methods=["GET"]) # TODO: remove
458
+ @api_login_required
459
+ def get_test_run_comment():
460
+ comment_id = request.args.get("commentId")
461
+ if not comment_id:
462
+ raise Exception("commentId wasn't specified in the request")
463
+ service = ArgusService()
464
+ comment = service.get_comment(comment_id=UUID(comment_id))
465
+ return jsonify({
466
+ "status": "ok",
467
+ "response": comment if comment else False
468
+ })
469
+
470
+
471
+ @bp.route("/users", methods=["GET"])
472
+ @api_login_required
473
+ def user_info():
474
+ result = UserService().get_users()
475
+
476
+ return jsonify({
477
+ "status": "ok",
478
+ "response": result
479
+ })
480
+
481
+
482
+ @bp.route("/release/stats/v2", methods=["GET"])
483
+ @api_login_required
484
+ def release_stats_v2():
485
+ request.query_string.decode(encoding="UTF-8")
486
+ release = request.args.get("release")
487
+ limited = bool(int(request.args.get("limited")))
488
+ version = request.args.get("productVersion", None)
489
+ include_no_version = bool(int(request.args.get("includeNoVersion", True)))
490
+ force = bool(int(request.args.get("force")))
491
+ stats = ReleaseStatsCollector(
492
+ release_name=release, release_version=version).collect(limited=limited, force=force, include_no_version=include_no_version)
493
+
494
+ res = jsonify({
495
+ "status": "ok",
496
+ "response": stats
497
+ })
498
+ res.cache_control.max_age = 300
499
+ return res
500
+
501
+
502
+ @bp.route("/test_runs/poll", methods=["GET"])
503
+ @api_login_required
504
+ def test_runs_poll():
505
+ limit = request.args.get("limit")
506
+ if not limit:
507
+ limit = 10
508
+ else:
509
+ limit = int(limit)
510
+ test_id = UUID(request.args.get('testId'))
511
+ additional_runs = [UUID(run) for run in request.args.getlist('additionalRuns[]')]
512
+ service = ArgusService()
513
+ runs = service.poll_test_runs(test_id=test_id, additional_runs=additional_runs, limit=limit)
514
+
515
+ return jsonify({
516
+ "status": "ok",
517
+ "response": runs
518
+ })
519
+
520
+
521
+ @bp.route("/test_run/poll", methods=["GET"])
522
+ @api_login_required
523
+ def test_run_poll_single():
524
+ runs = request.args.get("runs", "")
525
+ runs = [UUID(r) for r in runs.split(",") if r]
526
+ service = ArgusService()
527
+ test_runs = service.poll_test_runs_single(runs=runs)
528
+
529
+ return jsonify({
530
+ "status": "ok",
531
+ "response": test_runs
532
+ })
533
+
534
+
535
+ @bp.route("/release/create", methods=["POST"])
536
+ @api_login_required
537
+ def release_create():
538
+ if not request.is_json:
539
+ raise Exception(
540
+ "Content-Type mismatch, expected application/json, got:", request.content_type)
541
+ request_payload = request.get_json()
542
+ service = ArgusService()
543
+ result = service.create_release(request_payload)
544
+
545
+ return jsonify({
546
+ "status": "ok",
547
+ "response": result
548
+ })
549
+
550
+
551
+ @bp.route("/artifact/resolveSize")
552
+ @api_login_required
553
+ def resolve_artifact_size():
554
+ link = request.args.get("l")
555
+ if not link:
556
+ raise Exception("No link provided")
557
+
558
+ length = TestRunService().resolve_artifact_size(link)
559
+
560
+ return {
561
+ "status": "ok",
562
+ "response": {
563
+ "artifactSize": length,
564
+ }
565
+ }
566
+
567
+
568
+ @bp.route("/s3/<string:bucket_name>/<path:bucket_path>")
569
+ @api_login_required
570
+ def s3_generic_proxy(bucket_name: str, bucket_path: str):
571
+ service = TestRunService()
572
+ result = service.proxy_s3_image(
573
+ bucket_name=bucket_name,
574
+ bucket_path=bucket_path
575
+ )
576
+
577
+ return redirect(result, code=302)
578
+
579
+
580
+ @bp.route("/user/jobs")
581
+ @api_login_required
582
+ def user_jobs():
583
+ service = ArgusService()
584
+ result = list(service.get_jobs_for_user(user=g.user))
585
+
586
+ return {
587
+ "status": "ok",
588
+ "response": result
589
+ }
@@ -0,0 +1,67 @@
1
+ import os
2
+ import hashlib
3
+ from flask import (
4
+ Blueprint, flash, g, redirect, render_template, request, session, url_for, current_app
5
+ )
6
+ from werkzeug.security import check_password_hash
7
+ from argus.backend.models.web import User
8
+ from argus.backend.service.user import UserService, UserServiceException, load_logged_in_user, login_required
9
+
10
+ bp = Blueprint('auth', __name__, url_prefix='/auth')
11
+
12
+
13
+ @bp.route('/register', methods=('GET', 'POST'))
14
+ def register():
15
+ return redirect(url_for("auth.login"))
16
+
17
+
18
+ @bp.route('/login', methods=('GET', 'POST'))
19
+ def login():
20
+ token = hashlib.sha256((os.urandom(64))).hexdigest()
21
+ session["csrf_token"] = token
22
+
23
+ if request.method == 'POST':
24
+ try:
25
+ if "password" not in current_app.config.get("LOGIN_METHODS", []):
26
+ raise UserServiceException("Password Login is disabled")
27
+ username = request.form["username"]
28
+ password = request.form["password"]
29
+
30
+ try:
31
+ user: User = User.get(username=username)
32
+ except User.DoesNotExist:
33
+ raise UserServiceException("User not found")
34
+
35
+ if not check_password_hash(user.password, password):
36
+ raise UserServiceException("Incorrect Password")
37
+
38
+ session.clear()
39
+ session["user_id"] = str(user.id)
40
+ session["csrf_token"] = token
41
+ except UserServiceException as exc:
42
+ flash(next(iter(exc.args), "No message"), category="error")
43
+
44
+ return redirect(url_for('main.home'))
45
+
46
+ return render_template('auth/login.html.j2',
47
+ csrf_token=token,
48
+ github_cid=current_app.config.get("GITHUB_CLIENT_ID", "NO_CLIENT_ID"),
49
+ github_scopes=current_app.config.get("GITHUB_SCOPES", "user:email read:user read:org repo")
50
+ )
51
+
52
+
53
+ @bp.route("/profile/api/token/generate", methods=("POST",))
54
+ @login_required
55
+ def generate_api_token():
56
+ new_token = UserService().generate_token(g.user)
57
+ session["token_generated"] = new_token
58
+ return redirect(url_for('main.profile'))
59
+
60
+
61
+ @bp.route('/logout', methods=("POST",))
62
+ def logout():
63
+ session.clear()
64
+ return redirect(url_for('auth.login'))
65
+
66
+
67
+ bp.before_app_request(load_logged_in_user)