Skip to content

Commit 97ed6d7

Browse files
author
Jay Greguske
authored
Additional scheme endpoints for projects (#1295)
1 parent 009ae5e commit 97ed6d7

File tree

5 files changed

+199
-3
lines changed

5 files changed

+199
-3
lines changed

examples/maintenance.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616

1717
CI_JIRA_URL = os.environ["CI_JIRA_URL"]
1818
CI_JIRA_ADMIN = os.environ["CI_JIRA_ADMIN"]
19-
CI_JIRA_ADMIN_PASSWORD = os.environ["CI_JIRA_ADMIN_PASSWORD"]
19+
CI_JIRA_ADMIN_TOKEN = os.environ["CI_JIRA_ADMIN_TOKEN"]
2020

2121
j = JIRA(
2222
CI_JIRA_URL,
23-
basic_auth=(CI_JIRA_ADMIN, CI_JIRA_ADMIN_PASSWORD),
23+
basic_auth=(CI_JIRA_ADMIN, CI_JIRA_ADMIN_TOKEN),
2424
logging=True,
2525
validate=True,
2626
async_=True,

jira/client.py

+84
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,13 @@
6868
Issue,
6969
IssueLink,
7070
IssueLinkType,
71+
IssueSecurityLevelScheme,
7172
IssueType,
73+
IssueTypeScheme,
74+
NotificationScheme,
7275
PermissionScheme,
7376
Priority,
77+
PriorityScheme,
7478
Project,
7579
RemoteLink,
7680
RequestType,
@@ -86,6 +90,7 @@
8690
Version,
8791
Votes,
8892
Watchers,
93+
WorkflowScheme,
8994
Worklog,
9095
)
9196
from jira.utils import json_loads, threaded_requests
@@ -2133,6 +2138,32 @@ def votes(self, issue: str) -> Votes:
21332138
"""
21342139
return self._find_for_resource(Votes, issue)
21352140

2141+
@translate_resource_args
2142+
def project_issue_security_level_scheme(
2143+
self, project: str
2144+
) -> IssueSecurityLevelScheme:
2145+
"""Get a IssueSecurityLevelScheme Resource from the server.
2146+
2147+
Args:
2148+
project (str): ID or key of the project to get the IssueSecurityLevelScheme for
2149+
2150+
Returns:
2151+
IssueSecurityLevelScheme: The issue security level scheme
2152+
"""
2153+
return self._find_for_resource(IssueSecurityLevelScheme, project)
2154+
2155+
@translate_resource_args
2156+
def project_notification_scheme(self, project: str) -> NotificationScheme:
2157+
"""Get a NotificationScheme Resource from the server.
2158+
2159+
Args:
2160+
project (str): ID or key of the project to get the NotificationScheme for
2161+
2162+
Returns:
2163+
NotificationScheme: The notification scheme
2164+
"""
2165+
return self._find_for_resource(NotificationScheme, project)
2166+
21362167
@translate_resource_args
21372168
def project_permissionscheme(self, project: str) -> PermissionScheme:
21382169
"""Get a PermissionScheme Resource from the server.
@@ -2146,6 +2177,30 @@ def project_permissionscheme(self, project: str) -> PermissionScheme:
21462177
"""
21472178
return self._find_for_resource(PermissionScheme, project)
21482179

2180+
@translate_resource_args
2181+
def project_priority_scheme(self, project: str) -> PriorityScheme:
2182+
"""Get a PriorityScheme Resource from the server.
2183+
2184+
Args:
2185+
project (str): ID or key of the project to get the PriorityScheme for
2186+
2187+
Returns:
2188+
PriorityScheme: The priority scheme
2189+
"""
2190+
return self._find_for_resource(PriorityScheme, project)
2191+
2192+
@translate_resource_args
2193+
def project_workflow_scheme(self, project: str) -> WorkflowScheme:
2194+
"""Get a WorkflowScheme Resource from the server.
2195+
2196+
Args:
2197+
project (str): ID or key of the project to get the WorkflowScheme for
2198+
2199+
Returns:
2200+
WorkflowScheme: The workflow scheme
2201+
"""
2202+
return self._find_for_resource(WorkflowScheme, project)
2203+
21492204
@translate_resource_args
21502205
def add_vote(self, issue: str) -> Response:
21512206
"""Register a vote for the current authenticated user on an issue.
@@ -3989,6 +4044,21 @@ def permissionschemes(self):
39894044

39904045
return data["permissionSchemes"]
39914046

4047+
@lru_cache(maxsize=None)
4048+
def issue_type_schemes(self) -> List[IssueTypeScheme]:
4049+
"""Get all issue type schemes defined (Admin required).
4050+
4051+
Returns:
4052+
List[IssueTypeScheme]: All the Issue Type Schemes available to the currently logged in user.
4053+
"""
4054+
4055+
url = self._get_url("issuetypescheme")
4056+
4057+
r = self._session.get(url)
4058+
data: Dict[str, Any] = json_loads(r)
4059+
4060+
return data["schemes"]
4061+
39924062
@lru_cache(maxsize=None)
39934063
def issuesecurityschemes(self):
39944064

@@ -4075,6 +4145,20 @@ def delete_permissionscheme(self, id: str):
40754145
self.permissionschemes.cache_clear()
40764146
return data
40774147

4148+
def get_issue_type_scheme_associations(self, id: str) -> List[Project]:
4149+
"""For the specified issue type scheme, returns all of the associated projects. (Admin required)
4150+
4151+
Args:
4152+
id (str): The issue type scheme id.
4153+
4154+
Returns:
4155+
List[Project]: Associated Projects for the Issue Type Scheme.
4156+
"""
4157+
url = self._get_url(f"issuetypescheme/{id}/associations")
4158+
r = self._session.get(url)
4159+
data = json_loads(r)
4160+
return data
4161+
40784162
def create_project(
40794163
self,
40804164
key: str,

jira/resources.py

+68
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,14 @@ class AnyLike:
4242
"Worklog",
4343
"IssueLink",
4444
"IssueLinkType",
45+
"IssueSecurityLevelScheme",
4546
"IssueType",
47+
"IssueTypeScheme",
48+
"NotificationScheme",
4649
"Priority",
50+
"PriorityScheme",
4751
"Version",
52+
"WorkflowScheme",
4853
"Role",
4954
"Resolution",
5055
"SecurityLevel",
@@ -820,6 +825,40 @@ def __init__(
820825
self.raw: Dict[str, Any] = cast(Dict[str, Any], self.raw)
821826

822827

828+
class IssueTypeScheme(Resource):
829+
"""An issue type scheme."""
830+
831+
def __init__(self, options, session, raw=None):
832+
Resource.__init__(self, "issuetypescheme", options, session)
833+
if raw:
834+
self._parse_raw(raw)
835+
self.raw: Dict[str, Any] = cast(Dict[str, Any], self.raw)
836+
837+
838+
class IssueSecurityLevelScheme(Resource):
839+
"""IssueSecurityLevelScheme information on an project."""
840+
841+
def __init__(self, options, session, raw=None):
842+
Resource.__init__(
843+
self, "project/{0}/issuesecuritylevelscheme?expand=user", options, session
844+
)
845+
if raw:
846+
self._parse_raw(raw)
847+
self.raw: Dict[str, Any] = cast(Dict[str, Any], self.raw)
848+
849+
850+
class NotificationScheme(Resource):
851+
"""NotificationScheme information on an project."""
852+
853+
def __init__(self, options, session, raw=None):
854+
Resource.__init__(
855+
self, "project/{0}/notificationscheme?expand=user", options, session
856+
)
857+
if raw:
858+
self._parse_raw(raw)
859+
self.raw: Dict[str, Any] = cast(Dict[str, Any], self.raw)
860+
861+
823862
class PermissionScheme(Resource):
824863
"""Permissionscheme information on an project."""
825864

@@ -832,6 +871,30 @@ def __init__(self, options, session, raw=None):
832871
self.raw: Dict[str, Any] = cast(Dict[str, Any], self.raw)
833872

834873

874+
class PriorityScheme(Resource):
875+
"""PriorityScheme information on an project."""
876+
877+
def __init__(self, options, session, raw=None):
878+
Resource.__init__(
879+
self, "project/{0}/priorityscheme?expand=user", options, session
880+
)
881+
if raw:
882+
self._parse_raw(raw)
883+
self.raw: Dict[str, Any] = cast(Dict[str, Any], self.raw)
884+
885+
886+
class WorkflowScheme(Resource):
887+
"""WorkflowScheme information on an project."""
888+
889+
def __init__(self, options, session, raw=None):
890+
Resource.__init__(
891+
self, "project/{0}/workflowscheme?expand=user", options, session
892+
)
893+
if raw:
894+
self._parse_raw(raw)
895+
self.raw: Dict[str, Any] = cast(Dict[str, Any], self.raw)
896+
897+
835898
class Watchers(Resource):
836899
"""Watcher information on an issue."""
837900

@@ -1428,10 +1491,15 @@ def dict2resource(
14281491
r"issueLink/[^/]+$": IssueLink,
14291492
r"issueLinkType/[^/]+$": IssueLinkType,
14301493
r"issuetype/[^/]+$": IssueType,
1494+
r"issuetypescheme/[^/]+$": IssueTypeScheme,
1495+
r"project/[^/]+/issuesecuritylevelscheme[^/]+$": IssueSecurityLevelScheme,
1496+
r"project/[^/]+/notificationscheme[^/]+$": NotificationScheme,
1497+
r"project/[^/]+/priorityscheme[^/]+$": PriorityScheme,
14311498
r"priority/[^/]+$": Priority,
14321499
r"project/[^/]+$": Project,
14331500
r"project/[^/]+/role/[^/]+$": Role,
14341501
r"project/[^/]+/permissionscheme[^/]+$": PermissionScheme,
1502+
r"project/[^/]+/workflowscheme[^/]+$": WorkflowScheme,
14351503
r"resolution/[^/]+$": Resolution,
14361504
r"securitylevel/[^/]+$": SecurityLevel,
14371505
r"status/[^/]+$": Status,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from tests.conftest import JiraTestCase
2+
3+
4+
class IssueTypeSchemeAssociationTests(JiraTestCase):
5+
def test_scheme_associations(self):
6+
all_schemes = self.jira.issue_type_schemes()
7+
# there should be more than 1 scheme
8+
self.assertGreaterEqual(len(all_schemes), 2)
9+
test_pass = False
10+
for scheme in all_schemes:
11+
associations = self.jira.get_issue_type_scheme_associations(scheme["id"])
12+
# As long as one of these schemes is associated with a project-like object
13+
# we're probably ok.
14+
if len(associations) > 0:
15+
self.assertTrue(associations[0].get("id", False))
16+
self.assertTrue(associations[0].get("key", False))
17+
self.assertTrue(associations[0].get("lead", False))
18+
test_pass = True
19+
break
20+
self.assertTrue(test_pass)

tests/resources/test_project.py

+25-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from jira import JIRAError
12
from tests.conftest import JiraTestCase, find_by_id, rndstr
23

34

@@ -201,6 +202,29 @@ def test_project_roles(self):
201202
self.assertIn(user.name, [a.name for a in role.actors])
202203
self.assertIn(actor_admin, [a.name for a in role.actors])
203204

204-
def test_project_permissionscheme(self):
205+
def test_project_permission_scheme(self):
205206
permissionscheme = self.jira.project_permissionscheme(self.project_b)
206207
self.assertEqual(permissionscheme.name, "Default Permission Scheme")
208+
209+
def test_project_priority_scheme(self):
210+
priorityscheme = self.jira.project_priority_scheme(self.project_b)
211+
self.assertEqual(priorityscheme.name, "Default priority scheme")
212+
213+
def test_project_notification_scheme(self):
214+
notificationscheme = self.jira.project_notification_scheme(self.project_b)
215+
self.assertEqual(notificationscheme.name, "Default Notification Scheme")
216+
217+
def test_project_issue_security_level_scheme(self):
218+
# 404s are thrown when a project does not have an issue security scheme
219+
# associated with it explicitly. There are no ReST APIs for creating an
220+
# issue security scheme programmatically, so there is no way to test
221+
# this on the fly.
222+
with self.assertRaises(JIRAError):
223+
self.jira.project_issue_security_level_scheme(self.project_b)
224+
225+
def test_project_workflow_scheme(self):
226+
workflowscheme = self.jira.project_workflow_scheme(self.project_b)
227+
self.assertEqual(
228+
workflowscheme.name,
229+
f"{self.project_b}: Software Simplified Workflow Scheme",
230+
)

0 commit comments

Comments
 (0)