python-jira-plus 1.0.5__tar.gz → 1.0.7__tar.gz
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-1.0.5/python_jira_plus.egg-info → python_jira_plus-1.0.7}/PKG-INFO +1 -1
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/python_jira_plus/jira_plus.py +47 -13
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7/python_jira_plus.egg-info}/PKG-INFO +1 -1
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/setup.py +1 -1
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/LICENSE +0 -0
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/MANIFEST.in +0 -0
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/README.md +0 -0
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/pyproject.toml +0 -0
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/python_jira_plus/__init__.py +0 -0
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/python_jira_plus/describe_allowed_value.py +0 -0
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/python_jira_plus.egg-info/SOURCES.txt +0 -0
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/python_jira_plus.egg-info/dependency_links.txt +0 -0
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/python_jira_plus.egg-info/requires.txt +0 -0
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/python_jira_plus.egg-info/top_level.txt +0 -0
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/requirements.txt +0 -0
- {python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/setup.cfg +0 -0
|
@@ -14,6 +14,11 @@ from retrying import retry
|
|
|
14
14
|
from python_jira_plus.describe_allowed_value import describe_allowed_value
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
class UrlScheme(Enum):
|
|
18
|
+
HTTP = 'http'
|
|
19
|
+
HTTPS = 'https'
|
|
20
|
+
|
|
21
|
+
|
|
17
22
|
class ServerType(Enum):
|
|
18
23
|
CLOUD = 'cloud'
|
|
19
24
|
ON_PREMISE = 'on_premise'
|
|
@@ -27,7 +32,8 @@ class JiraPlus:
|
|
|
27
32
|
jira_username: Optional[str] = None,
|
|
28
33
|
jira_token: Optional[str] = None, # Use API token (not PAT)
|
|
29
34
|
verify_ssl: bool = True,
|
|
30
|
-
sso: bool = False
|
|
35
|
+
sso: bool = False,
|
|
36
|
+
url_scheme: UrlScheme = UrlScheme.HTTPS
|
|
31
37
|
) -> None:
|
|
32
38
|
self.logger = logging.getLogger()
|
|
33
39
|
logging.getLogger("urllib3").setLevel(urllib3_log_level)
|
|
@@ -35,9 +41,10 @@ class JiraPlus:
|
|
|
35
41
|
self.jira_username = jira_username or os.getenv('JIRA_USER_NAME')
|
|
36
42
|
self.jira_token = jira_token or os.getenv('JIRA_TOKEN')
|
|
37
43
|
self.base_url = base_url or os.getenv('JIRA_BASE_URL')
|
|
44
|
+
self.url_scheme = url_scheme.value
|
|
38
45
|
|
|
39
46
|
self.server_type = server_type
|
|
40
|
-
self.server = f'
|
|
47
|
+
self.server = f'{self.url_scheme}://{self.base_url}'
|
|
41
48
|
self.verify_ssl = verify_ssl
|
|
42
49
|
self.sso = sso
|
|
43
50
|
|
|
@@ -49,13 +56,13 @@ class JiraPlus:
|
|
|
49
56
|
if self.server_type == ServerType.ON_PREMISE and not self.sso:
|
|
50
57
|
jira_client = JIRA(
|
|
51
58
|
token_auth=self.jira_token,
|
|
52
|
-
options={'server': self.server},
|
|
59
|
+
options={'server': self.server, 'verify': self.verify_ssl},
|
|
53
60
|
timeout=timeout
|
|
54
61
|
)
|
|
55
62
|
else:
|
|
56
63
|
jira_client = JIRA(
|
|
57
64
|
basic_auth=(self.jira_username, self.jira_token),
|
|
58
|
-
options={'server': self.server},
|
|
65
|
+
options={'server': self.server, 'verify': self.verify_ssl},
|
|
59
66
|
timeout=timeout
|
|
60
67
|
)
|
|
61
68
|
|
|
@@ -190,13 +197,16 @@ class JiraPlus:
|
|
|
190
197
|
"""
|
|
191
198
|
Returns the allowed values for a specific field in a project + issue type context.
|
|
192
199
|
"""
|
|
193
|
-
|
|
200
|
+
fields_metadata = self.get_project_fields_metadata(
|
|
201
|
+
project_key=project_key,
|
|
202
|
+
issue_type=issue_type
|
|
203
|
+
)
|
|
194
204
|
|
|
195
205
|
# Try to find by ID or fallback to case-insensitive name
|
|
196
|
-
field =
|
|
206
|
+
field = fields_metadata.get(field_id_or_name)
|
|
197
207
|
if not field:
|
|
198
208
|
# Try matching by name (case-insensitive)
|
|
199
|
-
for fid, fmeta in
|
|
209
|
+
for fid, fmeta in fields_metadata.items():
|
|
200
210
|
if fmeta['name'].lower() == field_id_or_name.lower():
|
|
201
211
|
field = fmeta
|
|
202
212
|
break
|
|
@@ -218,28 +228,53 @@ class JiraPlus:
|
|
|
218
228
|
allowed_values = allowed_names | allowed_keys
|
|
219
229
|
return describe_allowed_value(value, allowed_values=allowed_values)
|
|
220
230
|
|
|
231
|
+
def get_server_version(self):
|
|
232
|
+
return tuple(self.jira_client.server_info()['versionNumbers'])
|
|
233
|
+
|
|
234
|
+
def check_server_createmeta_compatibility(self) -> bool:
|
|
235
|
+
server_version = self.get_server_version()
|
|
236
|
+
if self.server_type == ServerType.ON_PREMISE and server_version < (8, 0, 0):
|
|
237
|
+
self.logger.warning(
|
|
238
|
+
f"JIRA server version {server_version} is below the minimum required version (8.0.0). "
|
|
239
|
+
"Some features may not work as expected."
|
|
240
|
+
)
|
|
241
|
+
return False
|
|
242
|
+
return True
|
|
243
|
+
|
|
244
|
+
def get_project_fields_metadata(self, project_key: str, issue_type: str) -> dict:
|
|
245
|
+
if self.check_server_createmeta_compatibility():
|
|
246
|
+
meta, issue_meta = self._fetch_metadata(project_key=project_key, issue_type=issue_type)
|
|
247
|
+
return issue_meta['fields']
|
|
248
|
+
else:
|
|
249
|
+
_project_issue_types = self.jira_client.project_issue_types(project_key)
|
|
250
|
+
_issue_types = {t.name: t for t in _project_issue_types}
|
|
251
|
+
field_list = self.jira_client.project_issue_fields(project_key, _issue_types[issue_type].id)
|
|
252
|
+
return {f.fieldId: f.raw for f in field_list}
|
|
253
|
+
|
|
221
254
|
def validate_fields(
|
|
222
255
|
self,
|
|
223
256
|
project_key: str,
|
|
224
257
|
issue_type: str,
|
|
225
258
|
fields: Optional[dict] = None
|
|
226
259
|
) -> None:
|
|
227
|
-
|
|
228
|
-
|
|
260
|
+
fields_metadata = self.get_project_fields_metadata(
|
|
261
|
+
project_key=project_key,
|
|
262
|
+
issue_type=issue_type
|
|
263
|
+
)
|
|
229
264
|
for custom_field, value in fields.items():
|
|
230
|
-
if custom_field not in
|
|
265
|
+
if custom_field not in fields_metadata:
|
|
231
266
|
raise ValueError(
|
|
232
267
|
f'Invalid field(s) for project "{project_key}", issue type "{issue_type}": "{custom_field}"'
|
|
233
268
|
)
|
|
234
269
|
|
|
235
|
-
if allowed_value :=
|
|
270
|
+
if allowed_value := fields_metadata[custom_field].get('allowedValues'):
|
|
236
271
|
if not self._is_value_allowed(value=value, allowed_values=allowed_value):
|
|
237
272
|
raise ValueError(
|
|
238
273
|
f"Invalid value '{value}' for field '{custom_field}' in project '{project_key}' with issue type '{issue_type}'."
|
|
239
274
|
f"The allowed values are: {json.dumps(allowed_value, indent=4, sort_keys=True)}"
|
|
240
275
|
)
|
|
241
276
|
|
|
242
|
-
field_type =
|
|
277
|
+
field_type = fields_metadata[custom_field]['schema']['type']
|
|
243
278
|
if field_type == 'string' and not isinstance(value, str):
|
|
244
279
|
raise ValueError(f"Field '{custom_field}' must be a string")
|
|
245
280
|
elif field_type == 'number' and not isinstance(value, (int, float)):
|
|
@@ -253,7 +288,6 @@ class JiraPlus:
|
|
|
253
288
|
elif field_type == 'issuetype' and not isinstance(value, dict):
|
|
254
289
|
raise ValueError(f"Field '{custom_field}' must be an issue type")
|
|
255
290
|
# Add more field type checks as needed
|
|
256
|
-
return issue_meta['fields'].keys()
|
|
257
291
|
|
|
258
292
|
def create_issue(
|
|
259
293
|
self,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/python_jira_plus/describe_allowed_value.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_jira_plus-1.0.5 → python_jira_plus-1.0.7}/python_jira_plus.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|