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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-jira-plus
3
- Version: 1.0.5
3
+ Version: 1.0.7
4
4
  Summary: Enhanced Python client for JIRA with better error handling, pagination, and metadata validation.
5
5
  Home-page: https://github.com/aviz92/python-jira-plus
6
6
  Author: Avi Zaguri
@@ -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'https://{self.base_url}'
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
- _, issue_meta = self._fetch_metadata(project_key=project_key, issue_type=issue_type)
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 = issue_meta['fields'].get(field_id_or_name)
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 issue_meta['fields'].items():
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
- meta, issue_meta = self._fetch_metadata(project_key=project_key, issue_type=issue_type)
228
- field_types = meta['projects'][0]['issuetypes'][0]['fields']
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 issue_meta['fields']:
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 := issue_meta['fields'][custom_field].get('allowedValues'):
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 = field_types[custom_field]['schema']['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,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-jira-plus
3
- Version: 1.0.5
3
+ Version: 1.0.7
4
4
  Summary: Enhanced Python client for JIRA with better error handling, pagination, and metadata validation.
5
5
  Home-page: https://github.com/aviz92/python-jira-plus
6
6
  Author: Avi Zaguri
@@ -1,6 +1,6 @@
1
1
  from setuptools import setup, find_packages
2
2
 
3
- package_version = '1.0.5'
3
+ package_version = '1.0.7'
4
4
 
5
5
  package_name = 'python-jira-plus'
6
6
  package_description = 'Enhanced Python client for JIRA with better error handling, pagination, and metadata validation.'