22import logging
33
44from requests .exceptions import HTTPError
5-
65from .rest_client import AtlassianRestAPI
76
87log = logging .getLogger (__name__ )
@@ -62,7 +61,7 @@ def jql(self, jql, fields='*all', start=0, limit=None, expand=None):
6261 :param start: OPTIONAL: The start point of the collection to return. Default: 0.
6362 :param limit: OPTIONAL: The limit of the number of issues to return, this may be restricted by
6463 fixed system limits. Default by built-in method: 50
65- :param expand: OPTIONAL: expland the search result
64+ :param expand: OPTIONAL: expand the search result
6665 :return:
6766 """
6867 params = {}
@@ -306,18 +305,18 @@ def user_find_by_user_string(self, username, start=0, limit=50, include_inactive
306305 'maxResults' : limit
307306 }
308307 return self .get (url , params = params )
309-
308+
310309 def is_user_in_application (self , username , applicationKey ):
311310 """
312311 Utility function to test whether a user has an application role
313312 :param username: The username of the user to test.
314313 :param applicationKey: The application key of the application
315314 :return: True if the user has the application, else False
316315 """
317- user = self .user (username , 'applicationRoles' ) # Get applications roles of the user
316+ user = self .user (username , 'applicationRoles' ) # Get applications roles of the user
318317 if 'self' in user :
319318 for applicationRole in user .get ('applicationRoles' ).get ('items' ):
320- if applicationRole .get ('key' )== applicationKey :
319+ if applicationRole .get ('key' ) == applicationKey :
321320 return True
322321
323322 return False
@@ -334,8 +333,28 @@ def add_user_to_application(self, username, applicationKey):
334333 'username' : username ,
335334 'applicationKey' : applicationKey
336335 }
337- return self .post ('rest/api/2/user/application' , params = params )== None
338-
336+ return self .post ('rest/api/2/user/application' , params = params ) == None
337+
338+ # Application roles
339+ def get_all_application_roles (self ):
340+ """
341+ Returns all ApplicationRoles in the system
342+ :return:
343+ """
344+ url = 'rest/api/2/applicationrole'
345+
346+ return self .get (url )
347+
348+ def get_application_role (self , role_key ):
349+ """
350+ Returns the ApplicationRole with passed key if it exists
351+ :param role_key: str
352+ :return:
353+ """
354+ url = 'rest/api/2/applicationrole/{}' .format (role_key )
355+
356+ return self .get (url )
357+
339358 def projects (self , included_archived = None ):
340359 """Returns all projects which are visible for the currently logged in user.
341360 If no user is logged in, it returns the list of projects that are visible when using anonymous access.
@@ -527,6 +546,31 @@ def create_issue_type(self, name, description='', type='standard'):
527546 def issue (self , key , fields = '*all' ):
528547 return self .get ('rest/api/2/issue/{0}?fields={1}' .format (key , fields ))
529548
549+ def get_issue (self , issue_id_or_key , fields = None , properties = None , update_history = True ):
550+ """
551+ Returns a full representation of the issue for the given issue key
552+ By default, all fields are returned in this get-issue resource
553+
554+ :param issue_id_or_key: str
555+ :param fields: str
556+ :param properties: str
557+ :param update_history: bool
558+ :return: issue
559+ """
560+ url = 'rest/api/2/issue/{}' .format (issue_id_or_key )
561+ params = {}
562+
563+ if fields is not None :
564+ params ['fields' ] = fields
565+ if properties is not None :
566+ params ['properties' ] = properties
567+ if update_history is True :
568+ params ['updateHistory' ] = 'true'
569+ if update_history is False :
570+ params ['updateHistory' ] = 'false'
571+
572+ return self .get (url , params = params )
573+
530574 def bulk_issue (self , issue_list , fields = '*all' ):
531575 """
532576 :param fields:
@@ -886,11 +930,54 @@ def issue_deleted(self, issue_key):
886930 log .info ('Issue "{issue_key}" is deleted' .format (issue_key = issue_key ))
887931 return True
888932
933+ def delete_issue (self , issue_id_or_key , delete_subtasks = True ):
934+ """
935+ Delete an issue
936+ If the issue has subtasks you must set the parameter delete_subtasks = True to delete the issue
937+ You cannot delete an issue without its subtasks also being deleted
938+ :param issue_id_or_key:
939+ :param delete_subtasks:
940+ :return:
941+ """
942+ url = 'rest/api/2/issue/{}' .format (issue_id_or_key )
943+ params = {}
944+
945+ if delete_subtasks is True :
946+ params ['deleteSubtasks' ] = 'true'
947+ else :
948+ params ['deleteSubtasks' ] = 'false'
949+
950+ log .warning ('Removing issue {}...' .format (issue_id_or_key ))
951+
952+ return self .delete (url , params = params )
953+
954+ # @todo merge with edit_issue method
889955 def issue_update (self , issue_key , fields ):
890956 log .warning ('Updating issue "{issue_key}" with "{fields}"' .format (issue_key = issue_key , fields = fields ))
891957 url = 'rest/api/2/issue/{0}' .format (issue_key )
892958 return self .put (url , data = {'fields' : fields })
893959
960+ def edit_issue (self , issue_id_or_key , fields , notify_users = True ):
961+ """
962+ Edits an issue from a JSON representation
963+ The issue can either be updated by setting explicit the field
964+ value(s) or by using an operation to change the field value
965+
966+ :param issue_id_or_key: str
967+ :param fields: JSON
968+ :param notify_users: bool
969+ :return:
970+ """
971+ url = 'rest/api/2/issue/{}' .format (issue_id_or_key )
972+ params = {}
973+ data = {'update' : fields }
974+
975+ if notify_users is True :
976+ params ['notifyUsers' ] = 'true'
977+ else :
978+ params ['notifyUsers' ] = 'false'
979+ return self .put (url , data = data , params = params )
980+
894981 def issue_add_watcher (self , issue_key , user ):
895982 """
896983 Start watching issue
@@ -915,9 +1002,38 @@ def assign_issue(self, issue, assignee=None):
9151002
9161003 return self .put (url , data = data )
9171004
1005+ def create_issue (self , fields , update_history = False ):
1006+ """
1007+ Creates an issue or a sub-task from a JSON representation
1008+ :param fields: JSON data
1009+ :param update_history: bool (if true then the user's project history is updated)
1010+ :return:
1011+ """
1012+ url = 'rest/api/2/issue'
1013+ data = {'fields' : fields }
1014+ params = {}
1015+
1016+ if update_history is True :
1017+ params ['updateHistory' ] = 'true'
1018+ else :
1019+ params ['updateHistory' ] = 'false'
1020+ return self .post (url , params = params , data = data )
1021+
1022+ def create_issues (self , list_of_issues_data ):
1023+ """
1024+ Creates issues or sub-tasks from a JSON representation
1025+ Creates many issues in one bulk operation
1026+ :param list_of_issues_data: list of JSON data
1027+ :return:
1028+ """
1029+ url = 'rest/api/2/issue/bulk'
1030+ data = {'issueUpdates' : list_of_issues_data }
1031+ return self .post (url , data = data )
1032+
1033+ # @todo refactor and merge with create_issue method
9181034 def issue_create (self , fields ):
9191035 log .warning ('Creating issue "{summary}"' .format (summary = fields ['summary' ]))
920- url = 'rest/api/2/issue/ '
1036+ url = 'rest/api/2/issue'
9211037 return self .post (url , data = {'fields' : fields })
9221038
9231039 def issue_create_or_update (self , fields ):
@@ -950,20 +1066,49 @@ def issue_add_comment(self, issue_key, comment, visibility=None):
9501066 data ['visibility' ] = visibility
9511067 return self .post (url , data = data )
9521068
1069+ # Attachments
1070+ def get_attachment (self , attachment_id ):
1071+ """
1072+ Returns the meta-data for an attachment, including the URI of the actual attached file
1073+ :param attachment_id: int
1074+ :return:
1075+ """
1076+ url = 'rest/api/2/attachment/{}' .format (attachment_id )
1077+
1078+ return self .get (url )
1079+
1080+ def remove_attachment (self , attachment_id ):
1081+ """
1082+ Remove an attachment from an issue
1083+ :param attachment_id: int
1084+ :return: if success, return None
1085+ """
1086+ url = 'rest/api/2/attachment/{}' .format (attachment_id )
1087+
1088+ return self .delete (url )
1089+
1090+ def get_attachment_meta (self ):
1091+ """
1092+ Returns the meta information for an attachments,
1093+ specifically if they are enabled and the maximum upload size allowed
1094+ :return:
1095+ """
1096+ url = 'rest/api/2/attachment/meta'
1097+
1098+ return self .get (url )
1099+
9531100 def add_attachment (self , issue_key , filename ):
9541101 """
9551102 Add attachment to Issue
956-
9571103 :param issue_key: str
9581104 :param filename: str, name, if file in current directory or full path to file
9591105 """
9601106 log .warning ('Adding attachment...' )
9611107 headers = {'X-Atlassian-Token' : 'no-check' }
962- with open (filename , 'rb' ) as file :
963- files = {'file' : file }
964- url = 'rest/api/2/issue/{}/attachments' .format (issue_key )
965-
966- return self .post (url , headers = headers , files = files )
1108+ url = 'rest/api/2/issue/{}/attachments' .format (issue_key )
1109+ with open (filename , 'rb' ) as attachment :
1110+ files = {'file' : attachment }
1111+ return self .post (url , headers = headers , files = files )
9671112
9681113 def get_issue_remotelinks (self , issue_key , global_id = None , internal_id = None ):
9691114 """
@@ -1345,11 +1490,38 @@ def check_plugin_manager_status(self):
13451490 url = 'rest/plugins/latest/safe-mode'
13461491 return self .request (method = 'GET' , path = url , headers = headers )
13471492
1493+ # API/2 Get permissions
1494+ def get_permissions (self , project_id = None , project_key = None , issue_id = None , issue_key = None ):
1495+ """
1496+ Returns all permissions in the system and whether the currently logged in user has them.
1497+ You can optionally provide a specific context
1498+ to get permissions for (projectKey OR projectId OR issueKey OR issueId)
1499+
1500+ :param project_id: str
1501+ :param project_key: str
1502+ :param issue_id: str
1503+ :param issue_key: str
1504+ :return:
1505+ """
1506+ url = 'rest/api/2/mypermissions'
1507+ params = {}
1508+
1509+ if project_id :
1510+ params ['projectId' ] = project_id
1511+ if project_key :
1512+ params ['projectKey' ] = project_key
1513+ if issue_id :
1514+ params ['issueId' ] = issue_id
1515+ if issue_key :
1516+ params ['issueKey' ] = issue_key
1517+
1518+ return self .get (url , params = params )
1519+
13481520 def get_all_permissions (self ):
13491521 """
13501522 Returns all permissions that are present in the Jira instance -
13511523 Global, Project and the global ones added by plugins
1352- :return:
1524+ :return: All permissions
13531525 """
13541526 url = 'rest/api/2/permissions'
13551527 return self .get (url )
@@ -1455,6 +1627,43 @@ def get_project_issue_security_scheme(self, project_id_or_key, only_levels=False
14551627 else :
14561628 return self .get (url )
14571629
1630+ # Application properties
1631+ def get_property (self , key = None , permission_level = None , key_filter = None ):
1632+ """
1633+ Returns an application property
1634+
1635+ :param key: str
1636+ :param permission_level: str
1637+ :param key_filter: str
1638+ :return: list or item
1639+ """
1640+ url = 'rest/api/2/application-properties'
1641+ params = {}
1642+
1643+ if key :
1644+ params ['key' ] = key
1645+ if permission_level :
1646+ params ['permissionLevel' ] = permission_level
1647+ if key_filter :
1648+ params ['keyFilter' ] = key_filter
1649+
1650+ return self .get (url , params = params )
1651+
1652+ def set_property (self , property_id , value ):
1653+ url = 'rest/api/2/application-properties/{}' .format (property_id )
1654+ data = {'id' : property_id , 'value' : value }
1655+
1656+ return self .put (url , data = data )
1657+
1658+ def get_advanced_settings (self ):
1659+ """
1660+ Returns the properties that are displayed on the "General Configuration > Advanced Settings" page
1661+ :return:
1662+ """
1663+ url = 'rest/api/2/application-properties/advanced-settings'
1664+
1665+ return self .get (url )
1666+
14581667 """
14591668 #######################################################################
14601669 # Tempo Account REST API implements #
0 commit comments