Skip to content

Commit

Permalink
Add basic support for custom Jira fields
Browse files Browse the repository at this point in the history
In issue-config file one can newly set
custom Jira fields. These fields has to
use names as listed at /rest/api/2/field

Example:
  - summary: My test epic"
    type: epic
    fields:
      Labels:
       - "CY24Q3"
      "Pool Team": "my_great_team"
  • Loading branch information
kkaarreell committed Oct 10, 2024
1 parent b86ae7a commit 611a54a
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 20 deletions.
79 changes: 65 additions & 14 deletions newa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,7 @@ class IssueAction: # type: ignore[no-untyped-def]
job_recipe: Optional[str] = None
when: Optional[str] = None
newa_id: Optional[str] = None
fields: Optional[dict[str, str | float | list[str]]] = None


@define
Expand All @@ -1115,6 +1116,14 @@ class IssueConfig(Serializable): # type: ignore[no-untyped-def]
group: Optional[str] = None


@define
class JiraField:
id_: str
name: str
type_: Optional[str]
items: Optional[str]


@frozen
class IssueHandler:
""" An interface to Jira instance handling a specific ArtifactJob """
Expand All @@ -1127,12 +1136,9 @@ class IssueHandler:
# Each project can have different semantics of issue status.
transitions: dict[str, list[str]] = field()

# We assume that all projects have the following two custom fields mapped
# as follows.
custom_field_map: ClassVar[dict[str, str]] = {
"field_epic_link": "customfield_12311140",
"field_epic_name": "customfield_12311141",
}
# field name=>JiraField mapping will be obtained from Jira later
# see https://JIRASERVER/rest/api/2/field
field_map: ClassVar[dict[str, JiraField]] = {}

# Actual Jira connection.
connection: jira.JIRA = field(init=False)
Expand All @@ -1150,6 +1156,16 @@ def connection_factory(self) -> jira.JIRA:
# try connection first
try:
conn.myself()
# read field map from Jira and store its simplified version
fields = conn.fields()
for f in fields:
self.field_map[f['name']] = JiraField(
name=f['name'],
id_=f['id'],
type_=f['schema']['type'] if 'schema' in f else None,
items=f['schema']['items']
if ('schema' in f and 'items' in f['schema'])
else None)
except jira.JIRAError as e:
raise Exception('Could not authenticate to Jira. Wrong token?') from e
return conn
Expand Down Expand Up @@ -1270,7 +1286,8 @@ def create_issue(self,
description: str,
assignee_email: str | None = None,
parent: Issue | None = None,
group: Optional[str] = None) -> Issue:
group: Optional[str] = None,
fields: Optional[dict[str, str | float | list[str]]] = None) -> Issue:
""" Create issue """

data = {
Expand All @@ -1284,12 +1301,12 @@ def create_issue(self,
if action.type == IssueType.EPIC:
data |= {
"issuetype": {"name": "Epic"},
IssueHandler.custom_field_map["field_epic_name"]: data["summary"],
IssueHandler.field_map["Epic Name"].id_: data["summary"],
}
elif action.type == IssueType.TASK:
data |= {"issuetype": {"name": "Task"}}
if parent:
data |= {IssueHandler.custom_field_map["field_epic_link"]: parent.id}
data |= {IssueHandler.field_map["Epic Link"].id_: parent.id}
elif action.type == IssueType.SUBTASK:
if not parent:
raise Exception("Missing task while creating sub-task!")
Expand All @@ -1303,11 +1320,45 @@ def create_issue(self,

try:
jira_issue = self.connection.create_issue(data)
jira_issue.update(
fields={
"labels": [
*jira_issue.fields.labels,
IssueHandler.newa_label]})
if fields is None:
fields = {}
# always add NEWA label to fields
if "Labels" in fields and isinstance(fields['Labels'], list):
fields['Labels'].append(IssueHandler.newa_label)
else:
fields['Labels'] = [IssueHandler.newa_label]
# IssueHandler.field_map["Pool Team"].id_: [{"value":
# "sst_security_special_projects"}],
# populate fdata with configuration provided by the user
fdata: dict[str, str | float | list[Any]] = {}
for field in fields:
field_id = IssueHandler.field_map[field].id_
field_type = IssueHandler.field_map[field].type_
field_items = IssueHandler.field_map[field].items
value = fields[field]
# to ease processing set field_values to be always a list of strings
if isinstance(value, str):
field_values = [str(value)]
elif isinstance(value, list):
field_values = list(map(str, value))
else:
raise Exception(f'Unsupported Jira field conversion for {type(value)}')
# now we need to distinguish different types of fields and values
if field_type == 'string':
fdata[field_id] = field_values[0]
elif field_type == 'number':
fdata[field_id] = float(field_values[0])
elif field_type == 'array':
if field_items == 'string':
fdata[field_id] = field_values
elif field_items == 'option':
fdata[field_id] = [{"value": v} for v in field_values]
else:
raise Exception(f'Unsupported Jira field item {field_items}')
else:
raise Exception(f'Unsupported Jira field type {field_type}')

jira_issue.update(fields=fdata)
return Issue(jira_issue.key,
group=self.group,
summary=summary,
Expand Down
14 changes: 8 additions & 6 deletions newa/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,12 +607,14 @@ def _jira_fake_id_generator() -> Generator[str, int, None]:
if action.parent_id:
parent = processed_actions.get(action.parent_id, None)

new_issue = jira_handler.create_issue(action,
rendered_summary,
rendered_description,
rendered_assignee,
parent,
group=config.group)
new_issue = jira_handler.create_issue(
action,
rendered_summary,
rendered_description,
rendered_assignee,
parent,
group=config.group,
fields=action.fields)

processed_actions[action.id] = new_issue
created_action_ids.append(action.id)
Expand Down

0 comments on commit 611a54a

Please sign in to comment.