suite-py 1.52.0__py3-none-any.whl → 1.53.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.
suite_py/__version__.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # -*- encoding: utf-8 -*-
2
- __version__ = "1.52.0"
2
+ __version__ = "1.53.1"
@@ -10,11 +10,11 @@ from suite_py.lib.handler.youtrack_handler import YoutrackHandler
10
10
 
11
11
 
12
12
  class AskReview:
13
- def __init__(self, project, captainhook, config, tokens):
13
+ def __init__(self, project, backstage, config, tokens):
14
14
  self._project = project
15
15
  self._config = config
16
16
  self._youtrack = YoutrackHandler(config, tokens)
17
- self._captainhook = captainhook
17
+ self._backstage = backstage
18
18
  self._git = GitHandler(project, config)
19
19
  self._github = GithubHandler(tokens)
20
20
  self._frequent_reviewers = FrequentReviewersHandler(config)
@@ -32,13 +32,11 @@ class AskReview:
32
32
 
33
33
  def _maybe_get_users_list(self):
34
34
  try:
35
- users = self._captainhook.get_users_list().json()
35
+ users = self._backstage.get_users_list()
36
36
  self._config.put_cache("users", users)
37
37
  return users
38
38
  except Exception:
39
- logger.warning(
40
- "Can't get users list from Captainhook. Using cached version."
41
- )
39
+ logger.warning("Can't get users list from Backstage. Using cached version.")
42
40
  return self._config.get_cache("users")
43
41
 
44
42
  def _get_pr(self):
@@ -1,12 +1,15 @@
1
1
  # -*- encoding: utf-8 -*-
2
- import requests
2
+ import ssl
3
+ from urllib.parse import urlparse
3
4
 
5
+ import requests
4
6
  from suite_py.__version__ import __version__
5
7
  from suite_py.lib.handler.github_handler import GithubHandler
6
8
  from suite_py.lib.handler.okta_handler import Okta
9
+ from suite_py.lib import logger
7
10
 
8
11
 
9
- class BackstageResponseError(Exception): ...
12
+ class BackstageError(Exception): ...
10
13
 
11
14
 
12
15
  class Backstage:
@@ -14,18 +17,24 @@ class Backstage:
14
17
  _github: GithubHandler
15
18
 
16
19
  def __init__(self, config, okta: Okta, tokens=None):
17
- backstage_url = config.user["backstage_url"]
18
- self._baseurl = f"{backstage_url}/api/entity-locking"
20
+ self._backstage_url = config.user["backstage_url"]
19
21
  self._timeout = config.user["backstage_timeout"]
22
+ self._backstage_identity_token = None
23
+
24
+ # Under mise cacert are loaded from ~/.local/share/mise
25
+ # so we need to force system defaults to make warp happy
26
+ self._verify = ssl.get_default_verify_paths().capath
20
27
  self._okta = okta
21
28
 
22
29
  if tokens is not None:
23
30
  self._github = GithubHandler(tokens)
24
31
 
25
32
  def lock_project(self, project, env):
26
- return self.send_request(
27
- "post",
28
- "/projects/manage-lock",
33
+ return self._request(
34
+ method="post",
35
+ url=self._entity_locking_endpoint("/projects/manage-lock"),
36
+ headers=self._okta_headers(),
37
+ raise_for_status=True,
29
38
  json={
30
39
  "project": project,
31
40
  "status": "locked",
@@ -35,9 +44,11 @@ class Backstage:
35
44
  )
36
45
 
37
46
  def unlock_project(self, project, env):
38
- return self.send_request(
39
- "post",
40
- "/projects/manage-lock",
47
+ return self._request(
48
+ method="post",
49
+ url=self._entity_locking_endpoint("/projects/manage-lock"),
50
+ headers=self._okta_headers(),
51
+ raise_for_status=True,
41
52
  json={
42
53
  "project": project,
43
54
  "status": "unlocked",
@@ -47,36 +58,110 @@ class Backstage:
47
58
  )
48
59
 
49
60
  def status(self, project, env):
50
- return self.send_request(
51
- "get", f"/projects/check?project={project}&environment={env}"
61
+ return self._request(
62
+ method="get",
63
+ url=self._entity_locking_endpoint(
64
+ f"/projects/check?project={project}&environment={env}"
65
+ ),
66
+ raise_for_status=True,
52
67
  )
53
68
 
54
69
  def check(self):
55
- response = self.send_request("get", "/ping", timeout=(2, self._timeout))
70
+ response = self._request(
71
+ method="get",
72
+ url=self._entity_locking_endpoint("/ping"),
73
+ headers=self._okta_headers(),
74
+ timeout=(2, self._timeout),
75
+ raise_for_status=True,
76
+ )
77
+
56
78
  assert response.text == "pong"
57
79
 
58
- def send_request(self, method, endpoint, data=None, json=None, timeout=None):
80
+ def get_users_list(self):
81
+ response = self._fetch_users()
82
+
83
+ if response.status_code == 401:
84
+ self._refresh_backstage_identity_token()
85
+ response = self._fetch_users()
86
+
87
+ response.raise_for_status()
88
+
89
+ return [
90
+ {"name": u.pop("displayName"), **u}
91
+ for u in (item["spec"]["profile"] for item in response.json()["items"])
92
+ if "github" in u and "youtrack" in u and "displayName" in u
93
+ ]
94
+
95
+ def _request(self, method, url, **kwargs):
96
+ timeout = kwargs.pop("timeout", self._timeout)
97
+ verify = kwargs.pop("verify", self._verify)
98
+ allow_redirects = kwargs.pop("allow_redirects", False)
99
+ raise_for_status = kwargs.pop("raise_for_status", False)
100
+
59
101
  response = requests.request(
60
- method,
61
- f"{self._baseurl}{endpoint}",
62
- headers=self._headers(),
63
- data=data,
64
- json=json,
65
- timeout=(timeout or self._timeout),
102
+ method=method,
103
+ url=url,
104
+ timeout=timeout,
105
+ verify=verify,
106
+ allow_redirects=allow_redirects,
107
+ **kwargs,
66
108
  )
67
109
 
68
- if response.status_code == 200:
69
- return response
70
-
71
- raise BackstageResponseError(
72
- f"Got unexpected status code from Backstage: {response.status_code}"
110
+ redirect = (
111
+ urlparse(response.headers["Location"])
112
+ if response.status_code == 302
113
+ else None
73
114
  )
74
115
 
116
+ if redirect is not None and redirect.netloc == "prima.cloudflareaccess.com":
117
+ logger.error(
118
+ "It looks like Cloudflare WARP is not active, please enable it and try again"
119
+ )
120
+
121
+ raise BackstageError("Cloudflare Access authentication redirect")
122
+
123
+ if raise_for_status:
124
+ response.raise_for_status()
125
+
126
+ return response
127
+
128
+ def _entity_locking_endpoint(self, path):
129
+ return f"{self._backstage_url}/api/entity-locking{path}"
130
+
75
131
  def _get_user(self):
76
132
  return self._github.get_user().login
77
133
 
78
- def _headers(self):
134
+ def _okta_headers(self):
79
135
  return {
80
136
  "User-Agent": f"suite-py/{__version__}",
81
137
  "Authorization": f"Bearer {self._okta.get_id_token()}",
82
138
  }
139
+
140
+ def _backstage_headers(self):
141
+ if self._backstage_identity_token is None:
142
+ self._refresh_backstage_identity_token()
143
+
144
+ return {
145
+ "User-Agent": f"suite-py/{__version__}",
146
+ "Authorization": f"Bearer {self._backstage_identity_token}",
147
+ }
148
+
149
+ def _fetch_users(self):
150
+ fields = ",".join(
151
+ f"spec.profile.{f}" for f in ("displayName", "youtrack", "slack", "github")
152
+ )
153
+
154
+ return self._request(
155
+ method="GET",
156
+ url=f"{self._backstage_url}/api/catalog/entities/by-query?filter=kind=user&fields={fields}&limit=500",
157
+ headers=self._backstage_headers(),
158
+ )
159
+
160
+ def _refresh_backstage_identity_token(self):
161
+ response = self._request(
162
+ method="GET",
163
+ url=f"{self._backstage_url}/api/auth/cfaccess/refresh",
164
+ raise_for_status=True,
165
+ )
166
+
167
+ self._backstage_identity_token = response.json()["backstageIdentity"]["token"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: suite-py
3
- Version: 1.52.0
3
+ Version: 1.53.1
4
4
  Summary:
5
5
  Author: larrywax, EugenioLaghi, michelangelomo
6
6
  Author-email: devops@prima.it
@@ -1,8 +1,8 @@
1
1
  suite_py/__init__.py,sha256=REmi3D0X2G1ZWnYpKs8Ffm3NIj-Hw6dMuvz2b9NW344,142
2
- suite_py/__version__.py,sha256=aH4waNa4t_y13ssQMfeCqJjhsWfkkKeGmRRt8xfdQBo,49
2
+ suite_py/__version__.py,sha256=tWS55oa7CuidwTRjYZN1XICIW3isHmGkbR_Suy337-E,49
3
3
  suite_py/cli.py,sha256=FP9p8j3RDxcAd6vYJObysC7I3CsIwmRH2wf3sw4krQQ,12778
4
4
  suite_py/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- suite_py/commands/ask_review.py,sha256=yN__Ac-fiZBPShjRDhyCCQZGfVlQE16KozoJk4UtiNw,3788
5
+ suite_py/commands/ask_review.py,sha256=YptUKyac2eckhemv0FtQlJFoTAxS0IXAYkgokmKoxg8,3741
6
6
  suite_py/commands/bump.py,sha256=oFZU1hPfD11ujFC5G7wFyQOf2alY3xp2SO1h1ldjf3s,5406
7
7
  suite_py/commands/check.py,sha256=bRAiuGbafQV33dCZQQSNZ1zBYsecIX3E19iAQKbY86I,3684
8
8
  suite_py/commands/common.py,sha256=aWCEvO3hqdheuMUmZcHuc9EGZPQTk7VkzkHJk283MxQ,566
@@ -20,7 +20,7 @@ suite_py/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  suite_py/lib/config.py,sha256=YwimlbFJZh-K9Xrnmmw84SWy-GdfkFWSfnGl_wW5sII,4185
21
21
  suite_py/lib/handler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  suite_py/lib/handler/aws_handler.py,sha256=dRvRDicikfRbuFtCLPbevaX-yC-fO4LwXFdyqLPJ8OI,8815
23
- suite_py/lib/handler/backstage_handler.py,sha256=CGeuhlmNMUNka0-1nIxNDYw_teoey9ScWYArKzT-1J8,2370
23
+ suite_py/lib/handler/backstage_handler.py,sha256=pSdwrXOyIxrnKtowXc_Shhjpoh_x0ij3a6zWvYzO3lM,5231
24
24
  suite_py/lib/handler/captainhook_handler.py,sha256=R30_Vvh2ck7fM5fwbpm3UV_FtlQr2xnx6RJpkG1Gn14,2983
25
25
  suite_py/lib/handler/changelog_handler.py,sha256=-ppnRl3smBA_ys8tPqXmytS4eyntlwfawC2fiXFcwlw,4818
26
26
  suite_py/lib/handler/frequent_reviewers_handler.py,sha256=EIJX5FEMWzrxuXS9A17hu1vfxgJSOHSBX_ahCEZ2FVA,2185
@@ -41,7 +41,7 @@ suite_py/lib/requests/session.py,sha256=P32H3cWnCWunu91WIj2iDM5U3HzaBglg60VN_C9J
41
41
  suite_py/lib/symbol.py,sha256=z3QYBuNIwD3qQ3zF-cLOomIr_-C3bO_u5UIDAHMiyTo,60
42
42
  suite_py/lib/tokens.py,sha256=4DbsHDFLIxs40t3mRw_ZyhmejZQ0Bht7iAL8dTCTQd4,5458
43
43
  suite_py/templates/login.html,sha256=fJLls2SB84oZTSrxTdA5q1PqfvIHcCD4fhVWfyco7Ig,861
44
- suite_py-1.52.0.dist-info/METADATA,sha256=I-0R3K5j-1kejZ9jgu1n3bw_uM31h6sjP0HehU6IzSs,1188
45
- suite_py-1.52.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
46
- suite_py-1.52.0.dist-info/entry_points.txt,sha256=dVKLC-9Infy-dHJT_MkK6LcDjOgBCJ8lfPkURJhBjxE,46
47
- suite_py-1.52.0.dist-info/RECORD,,
44
+ suite_py-1.53.1.dist-info/METADATA,sha256=H8UmZJOHe_ERsn4A8LDR9cXbTkyqnfEEbWkojGFZ974,1188
45
+ suite_py-1.53.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
46
+ suite_py-1.53.1.dist-info/entry_points.txt,sha256=dVKLC-9Infy-dHJT_MkK6LcDjOgBCJ8lfPkURJhBjxE,46
47
+ suite_py-1.53.1.dist-info/RECORD,,