Skip to content

Commit e0e4ba5

Browse files
committed
Complete Phase 2: Implement remaining core methods
1 parent 43b2b2c commit e0e4ba5

File tree

3 files changed

+382
-10
lines changed

3 files changed

+382
-10
lines changed

atlassian/jira/cloud/adapter.py

Lines changed: 178 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ def _initialize_method_mapping(self) -> None:
6969
'issue_add_watcher': 'add_watcher',
7070
'issue_remove_watcher': 'remove_watcher',
7171
'jql_get': 'get_all_issues',
72+
# Adding newly implemented methods
73+
'get_custom_fields': 'get_custom_fields',
74+
'get_project_issues_count': 'get_project_issues_count',
75+
'get_all_project_issues': 'get_project_issues',
76+
'get_issue_remotelinks': 'get_issue_remotelinks',
77+
'get_issue_remote_links': 'get_issue_remotelinks',
78+
'get_issue_remote_link_by_id': 'get_issue_remote_link_by_id',
79+
'create_or_update_issue_remote_links': 'create_or_update_issue_remote_link'
7280
}
7381

7482
def __getattr__(self, name: str) -> Any:
@@ -208,4 +216,173 @@ def myself(self) -> Dict[str, Any]:
208216
DeprecationWarning,
209217
stacklevel=2,
210218
)
211-
return self.get_current_user()
219+
return self.get_current_user()
220+
221+
def get_project_issues_count(self, project_id_or_key: str) -> int:
222+
"""
223+
Legacy method to get the number of issues in a project.
224+
225+
Args:
226+
project_id_or_key: Project ID or key
227+
228+
Returns:
229+
Number of issues in the project
230+
"""
231+
warnings.warn(
232+
"The method get_project_issues_count is maintained for backward compatibility.",
233+
DeprecationWarning,
234+
stacklevel=2,
235+
)
236+
return super().get_project_issues_count(project_id_or_key)
237+
238+
def get_all_project_issues(
239+
self,
240+
project: str,
241+
fields: Union[str, List[str]] = "*all",
242+
start: int = 0,
243+
limit: Optional[int] = None
244+
) -> List[Dict[str, Any]]:
245+
"""
246+
Legacy method to get all issues in a project.
247+
248+
Args:
249+
project: Project key
250+
fields: Fields to include
251+
start: Start index
252+
limit: Maximum number of issues to return
253+
254+
Returns:
255+
List of issues
256+
"""
257+
warnings.warn(
258+
"The method get_all_project_issues is deprecated. Use get_project_issues instead.",
259+
DeprecationWarning,
260+
stacklevel=2,
261+
)
262+
return super().get_project_issues(project, fields=fields, start_at=start, max_results=limit)
263+
264+
def get_issue_remotelinks(
265+
self,
266+
issue_id_or_key: str,
267+
global_id: Optional[str] = None
268+
) -> List[Dict[str, Any]]:
269+
"""
270+
Legacy method to get remote links for an issue.
271+
272+
Args:
273+
issue_id_or_key: Issue ID or key
274+
global_id: Filter by global ID
275+
276+
Returns:
277+
List of remote links
278+
"""
279+
warnings.warn(
280+
"The method get_issue_remotelinks is maintained for backward compatibility.",
281+
DeprecationWarning,
282+
stacklevel=2,
283+
)
284+
return super().get_issue_remotelinks(issue_id_or_key, global_id)
285+
286+
def get_issue_remote_links(
287+
self,
288+
issue_id_or_key: str,
289+
global_id: Optional[str] = None
290+
) -> List[Dict[str, Any]]:
291+
"""
292+
Legacy method to get remote links for an issue.
293+
294+
Args:
295+
issue_id_or_key: Issue ID or key
296+
global_id: Filter by global ID
297+
298+
Returns:
299+
List of remote links
300+
"""
301+
warnings.warn(
302+
"The method get_issue_remote_links is deprecated. Use get_issue_remotelinks instead.",
303+
DeprecationWarning,
304+
stacklevel=2,
305+
)
306+
return super().get_issue_remotelinks(issue_id_or_key, global_id)
307+
308+
def get_issue_remote_link_by_id(
309+
self,
310+
issue_id_or_key: str,
311+
link_id: str
312+
) -> Dict[str, Any]:
313+
"""
314+
Legacy method to get a specific remote link for an issue.
315+
316+
Args:
317+
issue_id_or_key: Issue ID or key
318+
link_id: Remote link ID
319+
320+
Returns:
321+
Remote link details
322+
"""
323+
warnings.warn(
324+
"The method get_issue_remote_link_by_id is maintained for backward compatibility.",
325+
DeprecationWarning,
326+
stacklevel=2,
327+
)
328+
return super().get_issue_remote_link_by_id(issue_id_or_key, link_id)
329+
330+
def create_or_update_issue_remote_links(
331+
self,
332+
issue_id_or_key: str,
333+
link_url: str,
334+
title: str,
335+
global_id: Optional[str] = None,
336+
relationship: Optional[str] = None,
337+
icon_url: Optional[str] = None,
338+
icon_title: Optional[str] = None,
339+
status_resolved: bool = False,
340+
application: dict = {},
341+
) -> Dict[str, Any]:
342+
"""
343+
Legacy method to create or update a remote link for an issue.
344+
345+
Args:
346+
issue_id_or_key: Issue ID or key
347+
link_url: URL of the remote link
348+
title: Title of the remote link
349+
global_id: Global ID for the remote link (used for updates)
350+
relationship: Relationship of the link to the issue
351+
icon_url: URL of an icon for the link
352+
icon_title: Title for the icon
353+
status_resolved: Whether the remote link is resolved
354+
application: Application information
355+
356+
Returns:
357+
Created or updated remote link
358+
"""
359+
warnings.warn(
360+
"The method create_or_update_issue_remote_links is deprecated. "
361+
"Use create_or_update_issue_remote_link instead.",
362+
DeprecationWarning,
363+
stacklevel=2,
364+
)
365+
return super().create_or_update_issue_remote_link(
366+
issue_id_or_key=issue_id_or_key,
367+
link_url=link_url,
368+
title=title,
369+
global_id=global_id,
370+
relationship=relationship,
371+
icon_url=icon_url,
372+
icon_title=icon_title,
373+
status_resolved=status_resolved
374+
)
375+
376+
def get_projects(self) -> List[Dict[str, Any]]:
377+
"""
378+
Legacy method to get all projects.
379+
380+
Returns:
381+
List of all projects
382+
"""
383+
warnings.warn(
384+
"The method get_projects is deprecated. Use get_all_projects instead.",
385+
DeprecationWarning,
386+
stacklevel=2,
387+
)
388+
return list(super().get_all_projects())

atlassian/jira/cloud/cloud.py

Lines changed: 196 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,4 +633,199 @@ def get_current_user(self) -> Dict[str, Any]:
633633
Dictionary containing the current user data
634634
"""
635635
endpoint = self.get_endpoint("user_current")
636-
return self.get(endpoint)
636+
return self.get(endpoint)
637+
638+
def get_custom_fields(self) -> List[Dict[str, Any]]:
639+
"""
640+
Get all custom fields defined in the Jira instance.
641+
642+
Returns:
643+
List of custom field definitions
644+
"""
645+
endpoint = self.get_endpoint("field")
646+
647+
try:
648+
fields = self.get(endpoint)
649+
# Filter for custom fields only (custom fields have customfield_ prefix in their id)
650+
return [field for field in fields if field.get("id", "").startswith("customfield_")]
651+
except Exception as e:
652+
log.error(f"Failed to retrieve custom fields: {e}")
653+
raise
654+
655+
def get_project_issues(
656+
self,
657+
project_id_or_key: str,
658+
fields: Union[str, List[str]] = "*all",
659+
start_at: int = 0,
660+
max_results: Optional[int] = None
661+
) -> List[Dict[str, Any]]:
662+
"""
663+
Get all issues for a project.
664+
665+
Args:
666+
project_id_or_key: Project ID or key
667+
fields: Fields to include in the response (comma-separated string or list)
668+
start_at: Index of the first issue to return
669+
max_results: Maximum number of issues to return
670+
671+
Returns:
672+
List of issues in the project
673+
"""
674+
jql = f'project = "{project_id_or_key}" ORDER BY key'
675+
676+
# Handle fields parameter
677+
if isinstance(fields, list):
678+
fields = ",".join(fields)
679+
680+
# Get search results
681+
result = self.search_issues(
682+
jql=jql,
683+
start_at=start_at,
684+
max_results=max_results or 50,
685+
fields=fields
686+
)
687+
688+
return result.get("issues", [])
689+
690+
def get_project_issues_count(self, project_id_or_key: str) -> int:
691+
"""
692+
Get the number of issues in a project.
693+
694+
Args:
695+
project_id_or_key: Project ID or key
696+
697+
Returns:
698+
Number of issues in the project
699+
"""
700+
jql = f'project = "{project_id_or_key}"'
701+
702+
# Search with no fields to minimize response size
703+
result = self.search_issues(jql=jql, fields=["key"], max_results=1)
704+
705+
return result.get("total", 0)
706+
707+
def get_issue_remotelinks(
708+
self,
709+
issue_id_or_key: str,
710+
global_id: Optional[str] = None
711+
) -> List[Dict[str, Any]]:
712+
"""
713+
Get remote links for an issue.
714+
715+
Args:
716+
issue_id_or_key: Issue ID or key
717+
global_id: Filter by global ID
718+
719+
Returns:
720+
List of remote links
721+
"""
722+
issue_id_or_key = self.validate_id_or_key(issue_id_or_key, "issue_id_or_key")
723+
endpoint = self.get_endpoint("issue_remotelinks", id=issue_id_or_key)
724+
725+
params = {}
726+
if global_id:
727+
params["globalId"] = global_id
728+
729+
try:
730+
return self.get(endpoint, params=params)
731+
except Exception as e:
732+
log.error(f"Failed to retrieve remote links for issue {issue_id_or_key}: {e}")
733+
raise
734+
735+
def get_issue_watchers(self, issue_id_or_key: str) -> Dict[str, Any]:
736+
"""
737+
Get watchers for an issue.
738+
739+
Args:
740+
issue_id_or_key: Issue ID or key
741+
742+
Returns:
743+
Dictionary containing watchers information
744+
"""
745+
issue_id_or_key = self.validate_id_or_key(issue_id_or_key, "issue_id_or_key")
746+
endpoint = self.get_endpoint("issue_watchers", id=issue_id_or_key)
747+
748+
try:
749+
return self.get(endpoint)
750+
except Exception as e:
751+
log.error(f"Failed to retrieve watchers for issue {issue_id_or_key}: {e}")
752+
raise
753+
754+
def get_issue_remote_link_by_id(self, issue_id_or_key: str, link_id: str) -> Dict[str, Any]:
755+
"""
756+
Get a specific remote link for an issue.
757+
758+
Args:
759+
issue_id_or_key: Issue ID or key
760+
link_id: Remote link ID
761+
762+
Returns:
763+
Remote link details
764+
"""
765+
issue_id_or_key = self.validate_id_or_key(issue_id_or_key, "issue_id_or_key")
766+
endpoint = f"{self.get_endpoint('issue_remotelinks', id=issue_id_or_key)}/{link_id}"
767+
768+
try:
769+
return self.get(endpoint)
770+
except Exception as e:
771+
log.error(f"Failed to retrieve remote link {link_id} for issue {issue_id_or_key}: {e}")
772+
raise
773+
774+
def create_or_update_issue_remote_link(
775+
self,
776+
issue_id_or_key: str,
777+
link_url: str,
778+
title: str,
779+
global_id: Optional[str] = None,
780+
relationship: Optional[str] = None,
781+
icon_url: Optional[str] = None,
782+
icon_title: Optional[str] = None,
783+
status_resolved: bool = False
784+
) -> Dict[str, Any]:
785+
"""
786+
Create or update a remote link for an issue.
787+
788+
Args:
789+
issue_id_or_key: Issue ID or key
790+
link_url: URL of the remote link
791+
title: Title of the remote link
792+
global_id: Global ID for the remote link (used for updates)
793+
relationship: Relationship of the link to the issue
794+
icon_url: URL of an icon for the link
795+
icon_title: Title for the icon
796+
status_resolved: Whether the remote link is resolved
797+
798+
Returns:
799+
Created or updated remote link
800+
"""
801+
issue_id_or_key = self.validate_id_or_key(issue_id_or_key, "issue_id_or_key")
802+
endpoint = self.get_endpoint("issue_remotelinks", id=issue_id_or_key)
803+
804+
# Build the payload
805+
data = {
806+
"object": {
807+
"url": link_url,
808+
"title": title,
809+
"status": {"resolved": status_resolved}
810+
}
811+
}
812+
813+
if global_id:
814+
data["globalId"] = global_id
815+
816+
if relationship:
817+
data["relationship"] = relationship
818+
819+
if icon_url or icon_title:
820+
icon_data = {}
821+
if icon_url:
822+
icon_data["url16x16"] = icon_url
823+
if icon_title:
824+
icon_data["title"] = icon_title
825+
data["object"]["icon"] = icon_data
826+
827+
try:
828+
return self.post(endpoint, data=data)
829+
except Exception as e:
830+
log.error(f"Failed to create/update remote link for issue {issue_id_or_key}: {e}")
831+
raise

0 commit comments

Comments
 (0)