python-jira-plus 2.0.7__py3-none-any.whl → 2.0.8__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.
- python_jira_plus/__init__.py +2 -2
- python_jira_plus/describe_allowed_value.py +3 -3
- python_jira_plus/jira_plus.py +27 -22
- {python_jira_plus-2.0.7.dist-info → python_jira_plus-2.0.8.dist-info}/METADATA +9 -7
- python_jira_plus-2.0.8.dist-info/RECORD +8 -0
- python_jira_plus-2.0.7.dist-info/RECORD +0 -8
- {python_jira_plus-2.0.7.dist-info → python_jira_plus-2.0.8.dist-info}/WHEEL +0 -0
- {python_jira_plus-2.0.7.dist-info → python_jira_plus-2.0.8.dist-info}/licenses/LICENSE +0 -0
- {python_jira_plus-2.0.7.dist-info → python_jira_plus-2.0.8.dist-info}/top_level.txt +0 -0
python_jira_plus/__init__.py
CHANGED
|
@@ -8,7 +8,7 @@ pagination, metadata validation, and more.
|
|
|
8
8
|
from dotenv import load_dotenv
|
|
9
9
|
|
|
10
10
|
from python_jira_plus.describe_allowed_value import describe_allowed_value
|
|
11
|
-
from python_jira_plus.jira_plus import JiraPlus, ServerType
|
|
11
|
+
from python_jira_plus.jira_plus import JiraPlus, ServerType, UrlScheme
|
|
12
12
|
|
|
13
13
|
load_dotenv()
|
|
14
14
|
|
|
@@ -27,4 +27,4 @@ BASIC_FIELDS = [
|
|
|
27
27
|
"resolution",
|
|
28
28
|
"parent",
|
|
29
29
|
]
|
|
30
|
-
__all__ = ["JiraPlus", "ServerType", "BASIC_FIELDS"]
|
|
30
|
+
__all__ = ["JiraPlus", "ServerType", "UrlScheme", "BASIC_FIELDS"]
|
|
@@ -11,17 +11,17 @@ def describe_allowed_value(value: Any, allowed_values: set = None) -> bool:
|
|
|
11
11
|
|
|
12
12
|
@describe_allowed_value.register
|
|
13
13
|
def _(value: int, allowed_values: list = None) -> bool:
|
|
14
|
-
return value
|
|
14
|
+
return value in allowed_values
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
@describe_allowed_value.register
|
|
18
18
|
def _(value: float, allowed_values: list = None) -> bool:
|
|
19
|
-
return value
|
|
19
|
+
return value in allowed_values
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
@describe_allowed_value.register
|
|
23
23
|
def _(value: str, allowed_values: list = None) -> bool:
|
|
24
|
-
return value
|
|
24
|
+
return value in allowed_values
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@describe_allowed_value.register
|
python_jira_plus/jira_plus.py
CHANGED
|
@@ -53,10 +53,12 @@ class JiraPlus:
|
|
|
53
53
|
|
|
54
54
|
self.jira_client = self.create_connection()
|
|
55
55
|
self.check_client_connection()
|
|
56
|
+
self.server_version = self.get_server_version()
|
|
57
|
+
self.use_legacy_api = self._resolve_use_legacy_api()
|
|
56
58
|
|
|
57
59
|
@retry(stop_max_attempt_number=3, wait_fixed=180000)
|
|
58
|
-
def create_connection(self, timeout: int = 580) -> JIRA
|
|
59
|
-
if self.server_type == ServerType.ON_PREMISE and not self.sso:
|
|
60
|
+
def create_connection(self, timeout: int = 580) -> JIRA:
|
|
61
|
+
if self.server_type == ServerType.ON_PREMISE and not self.sso:
|
|
60
62
|
jira_client = JIRA(
|
|
61
63
|
token_auth=self.jira_token, options={"server": self.server, "verify": self.verify_ssl}, timeout=timeout
|
|
62
64
|
)
|
|
@@ -142,12 +144,16 @@ class JiraPlus:
|
|
|
142
144
|
all_issues = []
|
|
143
145
|
retries = 0
|
|
144
146
|
page = None
|
|
145
|
-
while True:
|
|
147
|
+
while True:
|
|
146
148
|
try:
|
|
149
|
+
next_token = None
|
|
150
|
+
if page is not None:
|
|
151
|
+
next_token = page.get('nextPageToken') if json_result else page.nextPageToken
|
|
152
|
+
|
|
147
153
|
self.logger.debug(f"Fetching issues from startAt={len(all_issues)}")
|
|
148
154
|
page = self.jira_client.enhanced_search_issues(
|
|
149
155
|
jql_str=query,
|
|
150
|
-
nextPageToken=
|
|
156
|
+
nextPageToken=next_token,
|
|
151
157
|
maxResults=max_results,
|
|
152
158
|
fields=specific_fields,
|
|
153
159
|
json_result=json_result,
|
|
@@ -156,7 +162,8 @@ class JiraPlus:
|
|
|
156
162
|
break
|
|
157
163
|
|
|
158
164
|
all_issues += page["issues"] if json_result else page
|
|
159
|
-
|
|
165
|
+
is_last = page['isLast'] if json_result else page.isLast
|
|
166
|
+
if is_last:
|
|
160
167
|
break
|
|
161
168
|
retries = 0 # reset on success
|
|
162
169
|
except JIRAError as err:
|
|
@@ -268,21 +275,16 @@ class JiraPlus:
|
|
|
268
275
|
def get_server_version(self) -> tuple[int, ...]:
|
|
269
276
|
return tuple(self.jira_client.server_info()["versionNumbers"])
|
|
270
277
|
|
|
271
|
-
def
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
)
|
|
277
|
-
self.logger.warning(
|
|
278
|
-
f"JIRA server version {server_version} is not fully compatible with createmeta-based field validation. "
|
|
279
|
-
"Some features may not work as expected."
|
|
280
|
-
)
|
|
281
|
-
return False
|
|
278
|
+
def _resolve_use_legacy_api(self) -> bool:
|
|
279
|
+
if self.server_type == ServerType.ON_PREMISE:
|
|
280
|
+
use_legacy = Version(".".join(str(x) for x in self.server_version)) < Version("9.0.0")
|
|
281
|
+
self.logger.debug(f"Using {'legacy createmeta' if use_legacy else 'project_issue_types'} API (ON_PREMISE {self.server_version})")
|
|
282
|
+
return use_legacy
|
|
283
|
+
self.logger.debug("Using legacy createmeta API (Cloud)")
|
|
282
284
|
return True
|
|
283
285
|
|
|
284
286
|
def get_project_fields_metadata(self, project_key: str, issue_type: str) -> dict:
|
|
285
|
-
if self.
|
|
287
|
+
if self.use_legacy_api:
|
|
286
288
|
_, issue_meta = self._fetch_metadata(project_key=project_key, issue_type=issue_type)
|
|
287
289
|
return issue_meta["fields"]
|
|
288
290
|
_project_issue_types = self.jira_client.project_issue_types(project_key)
|
|
@@ -291,7 +293,7 @@ class JiraPlus:
|
|
|
291
293
|
return {f.fieldId: f.raw for f in field_list}
|
|
292
294
|
|
|
293
295
|
def validate_fields( # pylint: disable=R1260
|
|
294
|
-
self, project_key: str, issue_type: str, fields: dict
|
|
296
|
+
self, project_key: str, issue_type: str, fields: dict
|
|
295
297
|
) -> None:
|
|
296
298
|
fields_metadata = self.get_project_fields_metadata(project_key=project_key, issue_type=issue_type)
|
|
297
299
|
for custom_field, value in fields.items():
|
|
@@ -370,7 +372,7 @@ class JiraPlus:
|
|
|
370
372
|
|
|
371
373
|
def delete_issue(self, issue_key: str) -> bool:
|
|
372
374
|
try:
|
|
373
|
-
issue = self.get_issue_by_key(key=issue_key)
|
|
375
|
+
issue = self.get_issue_by_key(key=issue_key, json_result=False)
|
|
374
376
|
issue.delete()
|
|
375
377
|
self.logger.info(f"Issue {issue_key} deleted successfully.")
|
|
376
378
|
return True
|
|
@@ -455,10 +457,13 @@ class JiraPlus:
|
|
|
455
457
|
return False
|
|
456
458
|
|
|
457
459
|
def get_account_data(self, user_mail: str) -> list[dict]:
|
|
458
|
-
|
|
459
|
-
f"
|
|
460
|
+
response = requests.get(
|
|
461
|
+
f"{self.server}/rest/api/3/user/search",
|
|
462
|
+
params={"query": user_mail},
|
|
463
|
+
auth=(self.jira_username, self.jira_token),
|
|
464
|
+
timeout=30,
|
|
460
465
|
)
|
|
461
|
-
response
|
|
466
|
+
response.raise_for_status()
|
|
462
467
|
return response.json()
|
|
463
468
|
|
|
464
469
|
def get_account_id(self, user_mail: str) -> str:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-jira-plus
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.8
|
|
4
4
|
Summary: Enhanced Python client for JIRA with better error handling, pagination, and metadata validation.
|
|
5
5
|
Author: Avi Zaguri
|
|
6
6
|
License: MIT
|
|
@@ -17,14 +17,16 @@ Requires-Python: >=3.12
|
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: custom-python-logger>=2.0.10
|
|
20
|
-
Requires-Dist: dotenv>=0.
|
|
20
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
21
21
|
Requires-Dist: jira>=3.10.5
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist: pytest>=9.0.1
|
|
22
|
+
Requires-Dist: packaging>=26.2
|
|
24
23
|
Requires-Dist: requests>=2.32.5
|
|
25
24
|
Requires-Dist: retrying>=1.4.2
|
|
26
|
-
|
|
27
|
-
Requires-Dist:
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pre-commit>=4.5.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest>=9.0.1; extra == "dev"
|
|
28
|
+
Requires-Dist: setuptools>=80.9.0; extra == "dev"
|
|
29
|
+
Requires-Dist: wheel>=0.45.1; extra == "dev"
|
|
28
30
|
Dynamic: license-file
|
|
29
31
|
|
|
30
32
|

|
|
@@ -59,7 +61,7 @@ pip install python-jira-plus
|
|
|
59
61
|
---
|
|
60
62
|
|
|
61
63
|
## Requirements
|
|
62
|
-
- Python 3.
|
|
64
|
+
- Python 3.12+
|
|
63
65
|
- `jira` package
|
|
64
66
|
- `retrying` package
|
|
65
67
|
- `custom-python-logger` package
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
python_jira_plus/__init__.py,sha256=BUKjGxQfkxWyZs-mKZs2bAgGnsYHQ4uOFjuB8MPmhfg,616
|
|
2
|
+
python_jira_plus/describe_allowed_value.py,sha256=fWfq0DkvJ2zmNIUXPH5rO5OgcY69bUqBIfTPQyxBt5E,1287
|
|
3
|
+
python_jira_plus/jira_plus.py,sha256=TNJkwFdDbkaVrsEaO3kBRGHi_GDFVhRcpoX6bAgyRTs,19568
|
|
4
|
+
python_jira_plus-2.0.8.dist-info/licenses/LICENSE,sha256=cSikHY6SZFsPZSBizCDAJ0-Bjjzxt-JtX6TVbKxwimo,1067
|
|
5
|
+
python_jira_plus-2.0.8.dist-info/METADATA,sha256=AdZUwcmuzp3qtoFVuaB1En8QQqjKxFz6_3U99X6G-o8,4285
|
|
6
|
+
python_jira_plus-2.0.8.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
7
|
+
python_jira_plus-2.0.8.dist-info/top_level.txt,sha256=3DxBdmf_naggoqDHSvLstG_GX3bW2XXYYorBDybBUiA,17
|
|
8
|
+
python_jira_plus-2.0.8.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
python_jira_plus/__init__.py,sha256=hi4PadXJu9vYDqslH0zxAm-UsU_zfVFHCImqbchHbDU,592
|
|
2
|
-
python_jira_plus/describe_allowed_value.py,sha256=ZU3SN0d3Xy_4qPaVhkhqQYabt3Q_tN64J2yJ3mV0Abc,1287
|
|
3
|
-
python_jira_plus/jira_plus.py,sha256=JcVeWBOHXQdfRbt13C8PH3fMSLxhKtCC1LtNRlS9eSc,19368
|
|
4
|
-
python_jira_plus-2.0.7.dist-info/licenses/LICENSE,sha256=cSikHY6SZFsPZSBizCDAJ0-Bjjzxt-JtX6TVbKxwimo,1067
|
|
5
|
-
python_jira_plus-2.0.7.dist-info/METADATA,sha256=jKNN8D42R_VO_D2WDSHV0I9e470npEOrDxHJVphr9yQ,4162
|
|
6
|
-
python_jira_plus-2.0.7.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
7
|
-
python_jira_plus-2.0.7.dist-info/top_level.txt,sha256=3DxBdmf_naggoqDHSvLstG_GX3bW2XXYYorBDybBUiA,17
|
|
8
|
-
python_jira_plus-2.0.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|