argus-alm 0.12.10__py3-none-any.whl → 0.13.0__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 (92) hide show
  1. argus/client/base.py +1 -1
  2. argus/client/driver_matrix_tests/cli.py +2 -2
  3. argus/client/driver_matrix_tests/client.py +1 -1
  4. argus/client/generic/cli.py +2 -2
  5. argus/client/sct/client.py +3 -3
  6. argus/client/sirenada/client.py +1 -1
  7. {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/METADATA +2 -4
  8. argus_alm-0.13.0.dist-info/RECORD +20 -0
  9. argus/backend/.gitkeep +0 -0
  10. argus/backend/cli.py +0 -41
  11. argus/backend/controller/__init__.py +0 -0
  12. argus/backend/controller/admin.py +0 -20
  13. argus/backend/controller/admin_api.py +0 -354
  14. argus/backend/controller/api.py +0 -529
  15. argus/backend/controller/auth.py +0 -67
  16. argus/backend/controller/client_api.py +0 -108
  17. argus/backend/controller/main.py +0 -274
  18. argus/backend/controller/notification_api.py +0 -72
  19. argus/backend/controller/notifications.py +0 -13
  20. argus/backend/controller/team.py +0 -126
  21. argus/backend/controller/team_ui.py +0 -18
  22. argus/backend/controller/testrun_api.py +0 -482
  23. argus/backend/controller/view_api.py +0 -162
  24. argus/backend/db.py +0 -100
  25. argus/backend/error_handlers.py +0 -21
  26. argus/backend/events/event_processors.py +0 -34
  27. argus/backend/models/__init__.py +0 -0
  28. argus/backend/models/result.py +0 -138
  29. argus/backend/models/web.py +0 -389
  30. argus/backend/plugins/__init__.py +0 -0
  31. argus/backend/plugins/core.py +0 -225
  32. argus/backend/plugins/driver_matrix_tests/controller.py +0 -63
  33. argus/backend/plugins/driver_matrix_tests/model.py +0 -421
  34. argus/backend/plugins/driver_matrix_tests/plugin.py +0 -22
  35. argus/backend/plugins/driver_matrix_tests/raw_types.py +0 -62
  36. argus/backend/plugins/driver_matrix_tests/service.py +0 -60
  37. argus/backend/plugins/driver_matrix_tests/udt.py +0 -42
  38. argus/backend/plugins/generic/model.py +0 -79
  39. argus/backend/plugins/generic/plugin.py +0 -16
  40. argus/backend/plugins/generic/types.py +0 -13
  41. argus/backend/plugins/loader.py +0 -40
  42. argus/backend/plugins/sct/controller.py +0 -185
  43. argus/backend/plugins/sct/plugin.py +0 -38
  44. argus/backend/plugins/sct/resource_setup.py +0 -178
  45. argus/backend/plugins/sct/service.py +0 -491
  46. argus/backend/plugins/sct/testrun.py +0 -272
  47. argus/backend/plugins/sct/udt.py +0 -101
  48. argus/backend/plugins/sirenada/model.py +0 -113
  49. argus/backend/plugins/sirenada/plugin.py +0 -17
  50. argus/backend/service/admin.py +0 -27
  51. argus/backend/service/argus_service.py +0 -688
  52. argus/backend/service/build_system_monitor.py +0 -188
  53. argus/backend/service/client_service.py +0 -122
  54. argus/backend/service/event_service.py +0 -18
  55. argus/backend/service/jenkins_service.py +0 -240
  56. argus/backend/service/notification_manager.py +0 -150
  57. argus/backend/service/release_manager.py +0 -230
  58. argus/backend/service/results_service.py +0 -317
  59. argus/backend/service/stats.py +0 -540
  60. argus/backend/service/team_manager_service.py +0 -83
  61. argus/backend/service/testrun.py +0 -559
  62. argus/backend/service/user.py +0 -307
  63. argus/backend/service/views.py +0 -258
  64. argus/backend/template_filters.py +0 -27
  65. argus/backend/tests/__init__.py +0 -0
  66. argus/backend/tests/argus_web.test.yaml +0 -39
  67. argus/backend/tests/conftest.py +0 -44
  68. argus/backend/tests/results_service/__init__.py +0 -0
  69. argus/backend/tests/results_service/test_best_results.py +0 -70
  70. argus/backend/util/common.py +0 -65
  71. argus/backend/util/config.py +0 -38
  72. argus/backend/util/encoders.py +0 -41
  73. argus/backend/util/logsetup.py +0 -81
  74. argus/backend/util/module_loaders.py +0 -30
  75. argus/backend/util/send_email.py +0 -91
  76. argus/client/generic_result_old.py +0 -143
  77. argus/db/.gitkeep +0 -0
  78. argus/db/argus_json.py +0 -14
  79. argus/db/cloud_types.py +0 -125
  80. argus/db/config.py +0 -135
  81. argus/db/db_types.py +0 -139
  82. argus/db/interface.py +0 -370
  83. argus/db/testrun.py +0 -740
  84. argus/db/utils.py +0 -15
  85. argus_alm-0.12.10.dist-info/RECORD +0 -96
  86. /argus/{backend → common}/__init__.py +0 -0
  87. /argus/{backend/util → common}/enums.py +0 -0
  88. /argus/{backend/plugins/sct/types.py → common/sct_types.py} +0 -0
  89. /argus/{backend/plugins/sirenada/types.py → common/sirenada_types.py} +0 -0
  90. {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/LICENSE +0 -0
  91. {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/WHEEL +0 -0
  92. {argus_alm-0.12.10.dist-info → argus_alm-0.13.0.dist-info}/entry_points.txt +0 -0
@@ -1,307 +0,0 @@
1
- from datetime import datetime
2
- import functools
3
- import hashlib
4
- import os
5
- import base64
6
- from uuid import UUID
7
- from time import time
8
- from hashlib import sha384
9
-
10
- from flask import current_app, flash, g, redirect, request, session, url_for
11
- import requests
12
- from werkzeug.security import generate_password_hash, check_password_hash
13
-
14
- from argus.backend.db import ScyllaCluster
15
- from argus.backend.error_handlers import APIException
16
- from argus.backend.models.web import User, UserOauthToken, UserRoles, WebFileStorage
17
- from argus.backend.util.common import FlaskView
18
-
19
-
20
- class UserServiceException(Exception):
21
- pass
22
-
23
- class GithubOrganizationMissingError(Exception):
24
- pass
25
-
26
-
27
- class UserService:
28
- # pylint: disable=no-self-use
29
- def __init__(self) -> None:
30
- self.cluster = ScyllaCluster.get()
31
- self.session = self.cluster.session
32
-
33
- @staticmethod
34
- def check_roles(roles: list[UserRoles] | UserRoles, user: User) -> bool:
35
- if not user:
36
- return False
37
- if isinstance(roles, str):
38
- return roles in user.roles
39
- elif isinstance(roles, list):
40
- for role in roles:
41
- if role in user.roles:
42
- return True
43
- return False
44
-
45
- def github_callback(self, req_code: str) -> dict | None:
46
- if "gh" not in current_app.config.get("LOGIN_METHODS", []):
47
- raise UserServiceException("Github Login is disabled")
48
- # pylint: disable=too-many-locals
49
- oauth_response = requests.post(
50
- "https://github.com/login/oauth/access_token",
51
- headers={
52
- "Accept": "application/json",
53
- },
54
- params={
55
- "code": req_code,
56
- "client_id": current_app.config.get("GITHUB_CLIENT_ID"),
57
- "client_secret": current_app.config.get("GITHUB_CLIENT_SECRET"),
58
- }
59
- )
60
-
61
- oauth_data = oauth_response.json()
62
-
63
- user_info = requests.get(
64
- "https://api.github.com/user",
65
- headers={
66
- "Accept": "application/json",
67
- "Authorization": f"token {oauth_data.get('access_token')}"
68
- }
69
- ).json()
70
- email_info = requests.get(
71
- "https://api.github.com/user/emails",
72
- headers={
73
- "Accept": "application/json",
74
- "Authorization": f"token {oauth_data.get('access_token')}"
75
- }
76
- ).json()
77
-
78
- organizations = requests.get(
79
- "https://api.github.com/user/orgs",
80
- headers={
81
- "Accept": "application/json",
82
- "Authorization": f"token {oauth_data.get('access_token')}"
83
- }
84
- ).json()
85
-
86
- temp_password = None
87
- required_organizations = current_app.config.get("GITHUB_REQUIRED_ORGANIZATIONS")
88
- if required_organizations:
89
- logins = set([org["login"] for org in organizations]) # pylint: disable=consider-using-set-comprehension
90
- required_organizations = set(required_organizations)
91
- if len(logins.intersection(required_organizations)) == 0:
92
- raise GithubOrganizationMissingError(
93
- "Not a member of a required organization or missing organization scope")
94
-
95
- try:
96
- user = User.get(username=user_info.get("login"))
97
- except User.DoesNotExist:
98
- user = User()
99
- user.username = user_info.get("login")
100
- # pick only scylladb.com emails
101
- scylla_email = next(iter([email.get("email") for email in email_info if email.get("email").endswith("@scylladb.com")]), None)
102
- primary_email = next(iter([email.get("email") for email in email_info if email.get("primary") and email.get("verified")]), None)
103
- user.email = scylla_email or primary_email
104
- user.full_name = user_info.get("name", user_info.get("login"))
105
- user.registration_date = datetime.utcnow()
106
- user.roles = ["ROLE_USER"]
107
- temp_password = base64.encodebytes(
108
- os.urandom(48)).decode("ascii").strip()
109
- user.password = generate_password_hash(temp_password)
110
-
111
- avatar_url: str = user_info.get("avatar_url")
112
- avatar = requests.get(avatar_url).content
113
- avatar_name = avatar_url.split("/")[-1]
114
- filename, filepath = self.save_profile_picture_to_disk(avatar_name, avatar, user.username)
115
-
116
- web_file = WebFileStorage()
117
- web_file.filename = filename
118
- web_file.filepath = filepath
119
- web_file.save()
120
- user.picture_id = web_file.id
121
- user.save()
122
-
123
- try:
124
- tokens = list(UserOauthToken.filter(user_id=user.id).all())
125
- github_token = [
126
- token for token in tokens if token["kind"] == "github"][0]
127
- github_token.token = oauth_data.get('access_token')
128
- github_token.save()
129
- except (UserOauthToken.DoesNotExist, IndexError):
130
- github_token = UserOauthToken()
131
- github_token.kind = "github"
132
- github_token.user_id = user.id
133
- github_token.token = oauth_data.get('access_token')
134
- github_token.save()
135
-
136
- redirect_target = session.get("redirect_target")
137
- session.clear()
138
- session["user_id"] = str(user.id)
139
- session["redirect_target"] = redirect_target
140
- if temp_password:
141
- return {
142
- "password": temp_password,
143
- "first_login": True
144
- }
145
- return None
146
-
147
- def get_users(self) -> dict:
148
- users = User.all()
149
- return {str(user.id): user.to_json() for user in users}
150
-
151
- def get_users_privileged(self) -> dict:
152
- users = User.all()
153
- users = {str(user.id): dict(user.items()) for user in users}
154
- for user in users.values():
155
- user.pop("password")
156
- user.pop("api_token")
157
-
158
- return users
159
-
160
- def generate_token(self, user: User):
161
- token_digest = f"{user.username}-{int(time())}-{base64.encodebytes(os.urandom(128)).decode(encoding='utf-8')}"
162
- new_token = base64.encodebytes(sha384(token_digest.encode(encoding="utf-8")
163
- ).digest()).decode(encoding="utf-8").strip()
164
- user.api_token = new_token
165
- user.save()
166
- return new_token
167
-
168
- def update_email(self, user: User, new_email: str):
169
- user.email = new_email
170
- user.save()
171
-
172
- return True
173
-
174
- def toggle_admin(self, user_id: str):
175
- user: User = User.get(id=user_id)
176
-
177
- if user.id == g.user.id:
178
- raise UserServiceException("Cannot toggle admin role from yourself.")
179
-
180
- is_admin = UserService.check_roles(UserRoles.Admin, user)
181
-
182
- if is_admin:
183
- user.roles.remove(UserRoles.Admin)
184
- else:
185
- user.set_as_admin()
186
-
187
- user.save()
188
- return True
189
-
190
- def delete_user(self, user_id: str):
191
- user: User = User.get(id=user_id)
192
- if user.id == g.user.id:
193
- raise UserServiceException("Cannot delete user that you are logged in as.")
194
-
195
- if user.is_admin():
196
- raise UserServiceException("Cannot delete admin users. Unset admin flag before deleting")
197
-
198
- user.delete()
199
-
200
- return True
201
-
202
- def update_password(self, user: User, old_password: str, new_password: str, force = False):
203
- if not check_password_hash(user.password, old_password) and not force:
204
- raise UserServiceException("Incorrect old password")
205
-
206
- if not new_password:
207
- raise UserServiceException("Empty new password")
208
-
209
- if len(new_password) < 5:
210
- raise UserServiceException("New password is too short")
211
-
212
- user.password = generate_password_hash(new_password)
213
- user.save()
214
-
215
- return True
216
-
217
- def update_name(self, user: User, new_name: str):
218
- user.full_name = new_name
219
- user.save()
220
-
221
- def save_profile_picture_to_disk(self, original_filename: str, filedata: bytes, suffix: str):
222
- filename_fragment = hashlib.sha256(os.urandom(64)).hexdigest()[:10]
223
- filename = f"profile_{suffix}_{filename_fragment}"
224
- filepath = f"storage/profile_pictures/{filename}"
225
- with open(filepath, "wb") as file:
226
- file.write(filedata)
227
-
228
- return original_filename, filepath
229
-
230
- def update_profile_picture(self, filename: str, filepath: str):
231
- web_file = WebFileStorage()
232
- web_file.filename = filename
233
- web_file.filepath = filepath
234
- web_file.save()
235
-
236
- try:
237
- if old_picture_id := g.user.picture_id:
238
- old_file = WebFileStorage.get(id=old_picture_id)
239
- os.unlink(old_file.filepath)
240
- old_file.delete()
241
- except Exception as exc: # pylint: disable=broad-except
242
- print(exc)
243
-
244
- g.user.picture_id = web_file.id
245
- g.user.save()
246
-
247
-
248
- def login_required(view: FlaskView):
249
- @functools.wraps(view)
250
- def wrapped_view(*args, **kwargs):
251
- if g.user is None and not getattr(view, "api_view", False):
252
- flash(message='Unauthorized, please login', category='error')
253
- session["redirect_target"] = request.full_path
254
- return redirect(url_for('auth.login'))
255
- elif g.user is None and getattr(view, "api_view", True):
256
- return {
257
- "status": "error",
258
- "message": "Authorization required"
259
- }, 403
260
-
261
- return view(*args, **kwargs)
262
-
263
- return wrapped_view
264
-
265
-
266
- def api_login_required(view: FlaskView):
267
- view.api_view = True
268
- return login_required(view)
269
-
270
-
271
- def check_roles(needed_roles: list[str] | str = None):
272
- def inner(view: FlaskView):
273
- @functools.wraps(view)
274
- def wrapped_view(*args, **kwargs):
275
- if not UserService.check_roles(needed_roles, g.user):
276
- flash(message='Not authorized to access this area', category='error')
277
- return redirect(url_for('main.home'))
278
-
279
- return view(*args, **kwargs)
280
-
281
- return wrapped_view
282
- return inner
283
-
284
-
285
- def load_logged_in_user():
286
- user_id = session.get('user_id')
287
- auth_header = request.headers.get("Authorization")
288
-
289
- if user_id:
290
- try:
291
- g.user = User.get(id=UUID(user_id)) # pylint: disable=assigning-non-slot
292
- return
293
- except User.DoesNotExist:
294
- session.clear()
295
-
296
- if auth_header:
297
- try:
298
- auth_schema, *auth_data = auth_header.split()
299
- if auth_schema == "token":
300
- token = auth_data[0]
301
- g.user = User.get(api_token=token)
302
- return
303
- except IndexError as exception:
304
- raise APIException("Malformed authorization header") from exception
305
- except User.DoesNotExist as exception:
306
- raise APIException("User not found for supplied token") from exception
307
- g.user = None # pylint: disable=assigning-non-slot
@@ -1,258 +0,0 @@
1
- import datetime
2
- import logging
3
- import re
4
- from functools import partial, reduce
5
- from typing import Any, Callable, TypedDict
6
- from urllib.parse import unquote
7
- from uuid import UUID
8
-
9
- from cassandra.cqlengine.models import Model
10
- from argus.backend.models.web import ArgusGroup, ArgusRelease, ArgusTest, ArgusUserView, User
11
- from argus.backend.plugins.loader import all_plugin_models
12
- from argus.backend.util.common import chunk, current_user
13
-
14
- LOGGER = logging.getLogger(__name__)
15
-
16
-
17
- class UserViewException(Exception):
18
- pass
19
-
20
-
21
- class ViewUpdateRequest(TypedDict):
22
- name: str
23
- description: str
24
- display_name: str
25
- tests: list[str]
26
- widget_settings: str
27
-
28
-
29
- class UserViewService:
30
- ADD_ALL_ID = UUID("db6f33b2-660b-4639-ba7f-79725ef96616")
31
- def create_view(self, name: str, items: list[str], widget_settings: str, description: str = None, display_name: str = None) -> ArgusUserView:
32
- try:
33
- name_check = ArgusUserView.get(name=name)
34
- raise UserViewException(f"View with name {name} already exists: {name_check.id}", name, name_check, name_check.id)
35
- except ArgusUserView.DoesNotExist:
36
- pass
37
- view = ArgusUserView()
38
- view.name = name
39
- view.display_name = display_name or name
40
- view.description = description
41
- view.widget_settings = widget_settings
42
- view.tests = []
43
- for entity in items:
44
- entity_type, entity_id = entity.split(":")
45
- match (entity_type):
46
- case "release":
47
- view.tests.extend(t.id for t in ArgusTest.filter(release_id=entity_id).all())
48
- view.release_ids.append(entity_id)
49
- case "group":
50
- view.tests.extend(t.id for t in ArgusTest.filter(group_id=entity_id).all())
51
- view.group_ids.append(entity_id)
52
- case "test":
53
- view.tests.append(entity_id)
54
- view.user_id = current_user().id
55
-
56
- view.save()
57
- return view
58
-
59
- @staticmethod
60
- def index_mapper(item: Model, type = "test"):
61
- mapped = dict(item)
62
- mapped["type"] = type
63
- return mapped
64
-
65
- def test_lookup(self, query: str):
66
- def check_visibility(entity: dict):
67
- if not entity["enabled"]:
68
- return False
69
- if entity.get("group") and not entity["group"]["enabled"]:
70
- return False
71
- if entity.get("release") and not entity["release"]["enabled"]:
72
- return False
73
- return True
74
-
75
- def facet_extraction(query: str) -> str:
76
- extractor = re.compile(r"(?:(?P<name>(?:release|group|type)):(?P<value>\"?[\w\d\.\-]*\"?))")
77
- facets = re.findall(extractor, query)
78
-
79
- return (re.sub(extractor, "", query).strip(), facets)
80
-
81
- def type_facet_filter(item: dict, key: str, facet_query: str):
82
- entity_type: str = item[key]
83
- return facet_query.lower() == entity_type
84
-
85
- def facet_filter(item: dict, key: str, facet_query: str):
86
- if entity := item.get(key):
87
- name: str = entity.get("pretty_name") or entity.get("name")
88
- return facet_query.lower() in name.lower() if name else False
89
- return False
90
-
91
- def facet_wrapper(query_func: Callable[[dict], bool], facet_query: str, facet_type: str) -> bool:
92
- def inner(item: dict, query: str):
93
- return query_func(item, query) and facet_funcs[facet_type](item, facet_type, facet_query)
94
- return inner
95
-
96
- facet_funcs = {
97
- "type": type_facet_filter,
98
- "release": facet_filter,
99
- "group": facet_filter,
100
- }
101
-
102
- def index_searcher(item, query: str):
103
- name: str = item["pretty_name"] or item["name"]
104
- return unquote(query).lower() in name.lower() if query else True
105
-
106
- text_query, facets = facet_extraction(query)
107
- search_func = index_searcher
108
- for facet, value in facets:
109
- if facet in facet_funcs.keys():
110
- search_func = facet_wrapper(query_func=search_func, facet_query=value, facet_type=facet)
111
-
112
-
113
- all_tests = ArgusTest.objects().limit(None)
114
- all_releases = ArgusRelease.objects().limit(None)
115
- all_groups = ArgusGroup.objects().limit(None)
116
- release_by_id = {release.id: partial(self.index_mapper, type="release")(release) for release in all_releases}
117
- group_by_id = {group.id: partial(self.index_mapper, type="group")(group) for group in all_groups}
118
- index = [self.index_mapper(t) for t in all_tests]
119
- index = [*release_by_id.values(), *group_by_id.values(), *index]
120
- for item in index:
121
- item["group"] = group_by_id.get(item.get("group_id"))
122
- item["release"] = release_by_id.get(item.get("release_id"))
123
-
124
- results = filter(partial(search_func, query=text_query), index)
125
-
126
- return [{ "id": self.ADD_ALL_ID, "name": "Add all...", "type": "special" }, *list(res for res in results if check_visibility(res))]
127
-
128
- def update_view(self, view_id: str | UUID, update_data: ViewUpdateRequest) -> bool:
129
- view: ArgusUserView = ArgusUserView.get(id=view_id)
130
- if view.user_id != current_user().id and not current_user().is_admin():
131
- raise UserViewException("Unable to modify other users' views")
132
- for key in ["user_id", "id"]:
133
- update_data.pop(key, None)
134
- items = update_data.pop("items")
135
- for k, value in update_data.items():
136
- view[k] = value
137
- view.tests = []
138
- view.release_ids = []
139
- view.group_ids = []
140
- for entity in items:
141
- entity_type, entity_id = entity.split(":")
142
- match (entity_type):
143
- case "release":
144
- view.tests.extend(t.id for t in ArgusTest.filter(release_id=entity_id).all())
145
- view.release_ids.append(entity_id)
146
- case "group":
147
- view.tests.extend(t.id for t in ArgusTest.filter(group_id=entity_id).all())
148
- view.group_ids.append(entity_id)
149
- case "test":
150
- view.tests.append(entity_id)
151
- view.last_updated = datetime.datetime.utcnow()
152
- view.save()
153
- return True
154
-
155
- def delete_view(self, view_id: str | UUID) -> bool:
156
- view = ArgusUserView.get(id=view_id)
157
- if view.user_id != current_user().id and not current_user().is_admin():
158
- raise UserViewException("Unable to modify other users' views")
159
- view.delete()
160
-
161
- return True
162
-
163
- def get_view(self, view_id: str | UUID) -> ArgusUserView:
164
- view: ArgusUserView = ArgusUserView.get(id=view_id)
165
- if datetime.datetime.utcnow() - (view.last_updated or datetime.datetime.fromtimestamp(0)) > datetime.timedelta(hours=1):
166
- self.refresh_stale_view(view)
167
- return view
168
-
169
- def get_view_by_name(self, view_name: str) -> ArgusUserView:
170
- view: ArgusUserView = ArgusUserView.get(name=view_name)
171
- if datetime.datetime.utcnow() - (view.last_updated or datetime.datetime.fromtimestamp(0)) > datetime.timedelta(hours=1):
172
- self.refresh_stale_view(view)
173
- return view
174
-
175
- def get_all_views(self, user: User | None = None) -> list[ArgusUserView]:
176
- if user:
177
- return list(ArgusUserView.filter(user_id=user.id).all())
178
- return list(ArgusUserView.filter().all())
179
-
180
- def resolve_view_tests(self, view_id: str | UUID) -> list[ArgusTest]:
181
- view = ArgusUserView.get(id=view_id)
182
- return self.resolve_tests_by_id(view.tests)
183
-
184
- def resolve_tests_by_id(self, test_ids: list[str | UUID]) -> list[ArgusTest]:
185
- tests = []
186
- for batch in chunk(test_ids):
187
- tests.extend(ArgusTest.filter(id__in=batch).all())
188
-
189
- return tests
190
-
191
- def batch_resolve_entity(self, entity: Model, param_name: str, entity_ids: list[UUID]) -> list[Model]:
192
- result = []
193
- for batch in chunk(entity_ids):
194
- result.extend(entity.filter(**{f"{param_name}__in": batch}).allow_filtering().all())
195
- return result
196
-
197
- def refresh_stale_view(self, view: ArgusUserView):
198
- view.tests = [test.id for test in self.resolve_view_tests(view.id)]
199
- all_tests = set(view.tests)
200
- all_tests.update(test.id for test in self.batch_resolve_entity(ArgusTest, "group_id", view.group_ids))
201
- all_tests.update(test.id for test in self.batch_resolve_entity(ArgusTest, "release_id", view.release_ids))
202
- view.tests = list(all_tests)
203
- view.last_updated = datetime.datetime.utcnow()
204
- view.save()
205
-
206
- return view
207
-
208
- def resolve_releases_for_tests(self, tests: list[ArgusTest]):
209
- releases = []
210
- unique_release_ids = reduce(lambda releases, test: releases.add(test.release_id) or releases, tests, set())
211
- for batch in chunk(unique_release_ids):
212
- releases.extend(ArgusRelease.filter(id__in=batch).all())
213
-
214
- return releases
215
-
216
- def resolve_groups_for_tests(self, tests: list[ArgusTest]):
217
- releases = []
218
- unique_release_ids = reduce(lambda groups, test: groups.add(test.group_id) or groups, tests, set())
219
- for batch in chunk(unique_release_ids):
220
- releases.extend(ArgusGroup.filter(id__in=batch).all())
221
-
222
- return releases
223
-
224
- def get_versions_for_view(self, view_id: str | UUID) -> list[str]:
225
- tests = self.resolve_view_tests(view_id)
226
- unique_versions = {ver for plugin in all_plugin_models()
227
- for ver in plugin.get_distinct_versions_for_view(tests=tests)}
228
-
229
- return sorted(list(unique_versions), reverse=True)
230
-
231
- def resolve_view_for_edit(self, view_id: str | UUID) -> dict:
232
- view: ArgusUserView = ArgusUserView.get(id=view_id)
233
- resolved = dict(view)
234
- view_groups = self.batch_resolve_entity(ArgusGroup, "id", view.group_ids)
235
- view_releases = self.batch_resolve_entity(ArgusRelease, "id", view.release_ids)
236
- view_tests = self.resolve_view_tests(view.id)
237
- all_groups = { group.id: partial(self.index_mapper, type="group")(group) for group in self.resolve_releases_for_tests(view_tests) }
238
- all_releases ={ release.id: partial(self.index_mapper, type="release")(release) for release in self.resolve_releases_for_tests(view_tests) }
239
- entities_by_id = {
240
- entity.id: partial(self.index_mapper, type="release" if isinstance(entity, ArgusRelease) else "group")(entity)
241
- for container in [view_releases, view_groups]
242
- for entity in container
243
- }
244
-
245
- items = []
246
- for test in view_tests:
247
- if not (entities_by_id.get(test.group_id) or entities_by_id.get(test.release_id)):
248
- item = dict(test)
249
- item["type"] = "test"
250
- items.append(item)
251
-
252
- items = [*entities_by_id.values(), *items]
253
- for entity in items:
254
- entity["group"] = all_groups.get(entity.get("group_id"), {}).get("pretty_name") or all_groups.get(entity.get("group_id"), {}).get("name")
255
- entity["release"] = all_releases.get(entity.get("release_id"), {}).get("pretty_name") or all_releases.get(entity.get("release_id"), {}).get("name")
256
-
257
- resolved["items"] = items
258
- return resolved
@@ -1,27 +0,0 @@
1
- from functools import partial
2
- from datetime import datetime
3
-
4
- from argus.backend.models.web import User
5
- from argus.backend.util.module_loaders import is_filter, export_functions
6
-
7
-
8
- export_filters = partial(export_functions, module_name=__name__, attr="is_filter")
9
-
10
-
11
- @is_filter("from_timestamp")
12
- def from_timestamp_filter(timestamp: int):
13
- return datetime.utcfromtimestamp(timestamp)
14
-
15
-
16
- @is_filter("safe_user")
17
- def safe_user(user: User):
18
- user_dict = dict(user.items())
19
- del user_dict["password"]
20
- return user_dict
21
-
22
-
23
- @is_filter("formatted_date")
24
- def formatted_date(date: datetime | None):
25
- if date:
26
- return date.strftime("%d/%m/%Y %H:%M:%S")
27
- return "#unknown"
File without changes
@@ -1,39 +0,0 @@
1
- # BASE URL FOR ARGUS APPLICATION
2
- BASE_URL: "https://argus.scylladb.com"
3
- # Main DB Cluster contact points
4
- SCYLLA_CONTACT_POINTS:
5
- - 172.18.0.2
6
- # Username
7
- SCYLLA_USERNAME: cassandra
8
- # Password
9
- SCYLLA_PASSWORD: cassandra
10
- # Default keyspace (can be created using 'create-keyspace' command with RF set to the number of contact points)
11
- SCYLLA_KEYSPACE_NAME: test_argus
12
- # Replication factor used - if set, will override contact_points as amount of nodes for replication
13
- # SCYLLA_REPLICATION_FACTOR: 3
14
- LOGIN_METHODS:
15
- - gh
16
- # Application log level
17
- APP_LOG_LEVEL: INFO
18
- # Secret key used to match session data
19
- SECRET_KEY: MUSTBEUNIQUE1
20
- # Client ID of a github oauth application
21
- GITHUB_CLIENT_ID: not_set
22
- # Scopes used for Github Application:
23
- # GITHUB_SCOPES: 'user:email read:user read:org repo'
24
- # Client secret of a github oauth application
25
- GITHUB_CLIENT_SECRET: not_set
26
- # Github personal access token
27
- GITHUB_ACCESS_TOKEN: unknown
28
- # List of required organization names (Comment out to disable organization requirement)
29
- GITHUB_REQUIRED_ORGANIZATIONS:
30
- # at least one is required for user to successfully authenticate
31
- BUILD_SYSTEM_FILTERED_PREFIXES:
32
- - prefixToExclude
33
- JENKINS_URL: https://jenkins.scylladb.com
34
- JENKINS_USER: not_set
35
- JENKINS_API_TOKEN_NAME: not_set
36
- JENKINS_API_TOKEN: not_set
37
- JENKINS_MONITORED_RELEASES:
38
- - not_set
39
-
@@ -1,44 +0,0 @@
1
- import os
2
- from pathlib import Path
3
- from unittest.mock import patch
4
-
5
- from _pytest.fixtures import fixture
6
-
7
- from argus.backend.cli import sync_models
8
- from argus.backend.db import ScyllaCluster
9
- from argus.backend.service.client_service import ClientService
10
- from argus.backend.service.release_manager import ReleaseManagerService
11
- from argus.backend.util.config import Config
12
- import logging
13
- os.environ['CQLENG_ALLOW_SCHEMA_MANAGEMENT'] = '1'
14
- logging.getLogger('cassandra').setLevel(logging.WARNING)
15
- logging.getLogger('cassandra.connection').setLevel(logging.WARNING)
16
- logging.getLogger('cassandra.pool').setLevel(logging.WARNING)
17
- logging.getLogger('cassandra.cluster').setLevel(logging.WARNING)
18
-
19
- def truncate_all_tables(session):
20
- for table in session.cluster.metadata.keyspaces[session.keyspace].tables:
21
- session.execute(f"TRUNCATE {table}")
22
-
23
-
24
- @fixture(autouse=True, scope='session')
25
- def argus_db():
26
- Config.CONFIG_PATHS = [Path(__file__).parent / "argus_web.test.yaml"]
27
- config = Config.load_yaml_config()
28
- database = ScyllaCluster.get(config)
29
- session = database.cluster.connect(keyspace=config["SCYLLA_KEYSPACE_NAME"])
30
- ScyllaCluster.get_session = lambda: session # monkey patching to escape need for flask app context
31
-
32
- sync_models()
33
- truncate_all_tables(database.session)
34
- yield database
35
- database.shutdown()
36
-
37
-
38
- @fixture(autouse=True, scope='session')
39
- def release_manager_service(argus_db):
40
- return ReleaseManagerService()
41
-
42
- @fixture(autouse=True, scope='session')
43
- def client_service(argus_db):
44
- return ClientService()
File without changes