python-jira-plus 1.0.3__tar.gz → 1.0.5__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.3
3
+ Version: 1.0.5
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
@@ -21,33 +21,43 @@ class ServerType(Enum):
21
21
 
22
22
  class JiraPlus:
23
23
  def __init__(
24
- self, base_url: Optional[str] = None,
25
- server_type: ServerType = ServerType.CLOUD,
26
- urllib3_log_level: int = logging.WARNING
24
+ self, base_url: Optional[str] = None,
25
+ server_type: ServerType = ServerType.CLOUD,
26
+ urllib3_log_level: int = logging.WARNING,
27
+ jira_username: Optional[str] = None,
28
+ jira_token: Optional[str] = None, # Use API token (not PAT)
29
+ verify_ssl: bool = True,
30
+ sso: bool = False
27
31
  ) -> None:
28
32
  self.logger = logging.getLogger()
29
33
  logging.getLogger("urllib3").setLevel(urllib3_log_level)
30
34
 
31
- self.jira_username = os.getenv('JIRA_USER_NAME')
32
- self.jira_token = os.getenv('JIRA_TOKEN')
33
-
34
- if not base_url:
35
- self.base_url = os.getenv('JIRA_BASE_URL')
35
+ self.jira_username = jira_username or os.getenv('JIRA_USER_NAME')
36
+ self.jira_token = jira_token or os.getenv('JIRA_TOKEN')
37
+ self.base_url = base_url or os.getenv('JIRA_BASE_URL')
36
38
 
37
39
  self.server_type = server_type
38
40
  self.server = f'https://{self.base_url}'
41
+ self.verify_ssl = verify_ssl
42
+ self.sso = sso
39
43
 
40
44
  self.jira_client = self.create_connection()
41
45
  self.check_client_connection()
42
46
 
43
- @retry(stop_max_attempt_number=3,
44
- wait_fixed=180000) # Retry 3 times with a 3-min (180000) delay between each attempt
45
- def create_connection(self) -> Optional[JIRA]:
46
- jira_client = JIRA(
47
- basic_auth=(self.jira_username, self.jira_token),
48
- options={'server': self.server},
49
- timeout=580
50
- )
47
+ @retry(stop_max_attempt_number=3, wait_fixed=180000) # Retry 3 times with a 3-min (180000) delay between each attempt
48
+ def create_connection(self, timeout=580) -> Optional[JIRA]:
49
+ if self.server_type == ServerType.ON_PREMISE and not self.sso:
50
+ jira_client = JIRA(
51
+ token_auth=self.jira_token,
52
+ options={'server': self.server},
53
+ timeout=timeout
54
+ )
55
+ else:
56
+ jira_client = JIRA(
57
+ basic_auth=(self.jira_username, self.jira_token),
58
+ options={'server': self.server},
59
+ timeout=timeout
60
+ )
51
61
 
52
62
  if jira_client.projects():
53
63
  self.logger.info(f"Jira Connection Successful")
@@ -81,10 +91,10 @@ class JiraPlus:
81
91
  return False
82
92
 
83
93
  def get_issue_by_key(
84
- self,
85
- key: str,
86
- specific_fields: str | list[str] = '*all',
87
- json_result: bool = True
94
+ self,
95
+ key: str,
96
+ specific_fields: str | list[str] = '*all',
97
+ json_result: bool = True
88
98
  ) -> None | Issue | dict[str, Any]:
89
99
  try:
90
100
  jira_issue = self.jira_client.issue(id=key, fields=specific_fields)
@@ -99,12 +109,12 @@ class JiraPlus:
99
109
  return None
100
110
 
101
111
  def get_objects_by_query(
102
- self,
103
- query: str,
104
- max_results: int = 5000,
105
- specific_fields: Union[str, list[str]] = "*all",
106
- jira_err_count: int = 3,
107
- json_result: bool = True,
112
+ self,
113
+ query: str,
114
+ max_results: int = 5000,
115
+ specific_fields: Union[str, list[str]] = "*all",
116
+ jira_err_count: int = 3,
117
+ json_result: bool = True,
108
118
  ) -> Optional[Union[ResultList[Issue], dict]]:
109
119
  return self._paginate_query(
110
120
  query=query,
@@ -115,12 +125,12 @@ class JiraPlus:
115
125
  )
116
126
 
117
127
  def _paginate_query(
118
- self,
119
- query: str,
120
- max_results: int,
121
- specific_fields: Union[str, list[str]],
122
- jira_err_limit: int,
123
- json_result: bool
128
+ self,
129
+ query: str,
130
+ max_results: int,
131
+ specific_fields: Union[str, list[str]],
132
+ jira_err_limit: int,
133
+ json_result: bool
124
134
  ) -> Union[ResultList[Issue] | list]:
125
135
  start_at = 0
126
136
  all_issues = []
@@ -172,10 +182,10 @@ class JiraPlus:
172
182
  return meta, issue_meta
173
183
 
174
184
  def get_allowed_values(
175
- self,
176
- project_key: str,
177
- issue_type: str,
178
- field_id_or_name: str,
185
+ self,
186
+ project_key: str,
187
+ issue_type: str,
188
+ field_id_or_name: str,
179
189
  ) -> Optional[list]:
180
190
  """
181
191
  Returns the allowed values for a specific field in a project + issue type context.
@@ -209,10 +219,10 @@ class JiraPlus:
209
219
  return describe_allowed_value(value, allowed_values=allowed_values)
210
220
 
211
221
  def validate_fields(
212
- self,
213
- project_key: str,
214
- issue_type: str,
215
- fields: Optional[dict] = None
222
+ self,
223
+ project_key: str,
224
+ issue_type: str,
225
+ fields: Optional[dict] = None
216
226
  ) -> None:
217
227
  meta, issue_meta = self._fetch_metadata(project_key=project_key, issue_type=issue_type)
218
228
  field_types = meta['projects'][0]['issuetypes'][0]['fields']
@@ -246,13 +256,13 @@ class JiraPlus:
246
256
  return issue_meta['fields'].keys()
247
257
 
248
258
  def create_issue(
249
- self,
250
- project_key: str,
251
- summary: str,
252
- description: str,
253
- issue_type: str,
254
- assignee: Optional[str] = None,
255
- custom_fields: Optional[dict] = None,
259
+ self,
260
+ project_key: str,
261
+ summary: str,
262
+ description: str,
263
+ issue_type: str,
264
+ assignee: Optional[str] = None,
265
+ custom_fields: Optional[dict] = None,
256
266
  ) -> Optional[Issue]:
257
267
  issue_fields = {
258
268
  "project": {"key": project_key},
@@ -261,7 +271,8 @@ class JiraPlus:
261
271
  "description": description,
262
272
  }
263
273
  if assignee:
264
- issue_fields["assignee"] = {"accountId": assignee} if self.server_type == ServerType.CLOUD else {"name": assignee}
274
+ issue_fields["assignee"] = {"accountId": assignee} if self.server_type == ServerType.CLOUD else {
275
+ "name": assignee}
265
276
 
266
277
  if custom_fields:
267
278
  issue_fields = issue_fields | custom_fields
@@ -279,9 +290,9 @@ class JiraPlus:
279
290
  return None
280
291
 
281
292
  def update_issue(
282
- self,
283
- issue_key: str,
284
- fields_to_update: dict
293
+ self,
294
+ issue_key: str,
295
+ fields_to_update: dict
285
296
  ) -> Optional[Issue]:
286
297
  try:
287
298
  issue = self.jira_client.issue(issue_key)
@@ -350,11 +361,11 @@ class JiraPlus:
350
361
  self.logger.exception(f"Exception - Attachment {file_path} was not uploaded to issue {issue_key}\n{err}")
351
362
 
352
363
  def transition_issue(
353
- self,
354
- issue_key: str,
355
- transition_name: str,
356
- fields: Optional[dict] = None,
357
- comment: Optional[str] = None,
364
+ self,
365
+ issue_key: str,
366
+ transition_name: str,
367
+ fields: Optional[dict] = None,
368
+ comment: Optional[str] = None,
358
369
  ) -> bool:
359
370
  try:
360
371
  transitions = self.jira_client.transitions(issue_key)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-jira-plus
3
- Version: 1.0.3
3
+ Version: 1.0.5
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.3'
3
+ package_version = '1.0.5'
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.'