From 463e56fad55ea91184afe164ff53ddc7373cd616 Mon Sep 17 00:00:00 2001 From: Krzysztof Szarek Date: Tue, 11 Aug 2026 12:46:06 +0200 Subject: [PATCH] feat(jira): expose issue metadata --- CHANGES.md | 4 ++ actions/lib/formatters.py | 24 +++++-- pack.yaml | 2 +- tests/test_formatters.py | 146 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+), 8 deletions(-) create mode 100644 tests/test_formatters.py diff --git a/CHANGES.md b/CHANGES.md index fa03559..392f4d3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Change Log +# 3.4.0 +- Add issue type and status category metadata to issue output, and issue type metadata to + linked issue output. + # 3.3.0 - Added new ``search_users`` action. Addresses [#90](https://github.com/StackStorm-Exchange/stackstorm-jira/issues/90) diff --git a/actions/lib/formatters.py b/actions/lib/formatters.py index 05b92fd..07d1a50 100644 --- a/actions/lib/formatters.py +++ b/actions/lib/formatters.py @@ -28,6 +28,9 @@ def to_issue_dict(issue, include_comments=False, include_attachments=False, else: assignee = None + issue_type = getattr(issue.fields, 'issuetype', None) + status_category = getattr(issue.fields.status, 'statusCategory', None) + result = { 'id': issue.id, 'key': issue.key, @@ -35,6 +38,8 @@ def to_issue_dict(issue, include_comments=False, include_attachments=False, 'summary': issue.fields.summary, 'description': issue.fields.description if hasattr(issue.fields, 'description') else None, 'status': issue.fields.status.name, + 'issue_type': issue_type.name if issue_type else None, + 'status_category': status_category.name if status_category else None, 'priority': issue.fields.priority.name if hasattr(issue.fields, 'priority') else None, 'resolution': resolution, 'labels': issue.fields.labels if hasattr(issue.fields, 'labels') else [], @@ -129,15 +134,20 @@ def to_links_dict(issue): """ :rtype: ``dict`` """ + outward_issue = issue.raw.get('outwardIssue') + linked_issue = outward_issue or issue.raw.get('inwardIssue') or {} + fields = linked_issue.get('fields') or {} + status = fields.get('status') or {} + issue_type = fields.get('issuetype') or {} + link_type = issue.raw.get('type') or {} + result = { 'id': issue.raw.get('id'), - 'key': issue.raw.get('outwardIssue', issue.raw.get('inwardIssue')).get('key'), - 'summary': issue.raw.get('outwardIssue', issue.raw.get('inwardIssue')) - .get('fields').get('summary'), - 'status': issue.raw.get('outwardIssue', issue.raw.get('inwardIssue')).get('fields') - .get('status').get('name'), - 'type': issue.raw.get('type').get('outward') if issue.raw.get('outwardIssue') - else issue.raw.get('type').get('inward'), + 'key': linked_issue.get('key'), + 'summary': fields.get('summary'), + 'status': status.get('name'), + 'type': link_type.get('outward') if outward_issue else link_type.get('inward'), + 'issue_type': issue_type.get('name'), } return result diff --git a/pack.yaml b/pack.yaml index b27e9db..74a8c20 100644 --- a/pack.yaml +++ b/pack.yaml @@ -6,7 +6,7 @@ keywords: - issues - ticket management - project management -version: 3.3.0 +version: 3.4.0 python_versions: - "3" author: StackStorm, Inc. diff --git a/tests/test_formatters.py b/tests/test_formatters.py new file mode 100644 index 0000000..f917879 --- /dev/null +++ b/tests/test_formatters.py @@ -0,0 +1,146 @@ +from functools import partial +from types import SimpleNamespace + +from lib.formatters import to_issue_dict, to_links_dict + + +def test_to_issue_dict_includes_metadata() -> None: + fields = SimpleNamespace( + assignee=SimpleNamespace(displayName="Assignee"), + created="2026-07-29T10:00:00.000+0000", + description="Description", + issuetype=SimpleNamespace(name="Task"), + labels=["example"], + priority=SimpleNamespace(name="High"), + reporter=SimpleNamespace(displayName="Reporter"), + resolution=SimpleNamespace(name="Done"), + resolutiondate="2026-07-30T10:00:00.000+0000", + status=SimpleNamespace( + name="Completed", + statusCategory=SimpleNamespace(name="Done"), + ), + summary="Example issue", + updated="2026-07-30T10:00:00.000+0000", + ) + issue = SimpleNamespace( + fields=fields, + id="10001", + key="TEST-1", + permalink=partial(str, "https://jira.example.com/browse/TEST-1 - Example issue"), + ) + + assert to_issue_dict(issue) == { + "id": "10001", + "key": "TEST-1", + "url": "https://jira.example.com/browse/TEST-1", + "summary": "Example issue", + "description": "Description", + "status": "Completed", + "issue_type": "Task", + "status_category": "Done", + "priority": "High", + "resolution": "Done", + "labels": ["example"], + "reporter": "Reporter", + "assignee": "Assignee", + "created_at": "2026-07-29T10:00:00.000+0000", + "updated_at": "2026-07-30T10:00:00.000+0000", + "resolved_at": "2026-07-30T10:00:00.000+0000", + } + + +def test_to_issue_dict_handles_missing_optional_metadata() -> None: + fields = SimpleNamespace( + assignee=None, + created="2026-07-29T10:00:00.000+0000", + description=None, + labels=[], + reporter=None, + resolution=None, + resolutiondate=None, + status=SimpleNamespace(name="Open"), + summary="Partial issue", + updated="2026-07-30T10:00:00.000+0000", + ) + issue = SimpleNamespace( + fields=fields, + id="10002", + key="TEST-2", + permalink=partial(str, "https://jira.example.com/browse/TEST-2 - Partial issue"), + ) + + result = to_issue_dict(issue) + + assert result["issue_type"] is None + assert result["status_category"] is None + + +def test_to_links_dict_includes_outward_issue_type() -> None: + link = SimpleNamespace( + raw={ + "id": "20001", + "outwardIssue": { + "key": "TEST-3", + "fields": { + "issuetype": {"name": "Task"}, + "status": {"name": "To Do"}, + "summary": "Linked issue", + }, + }, + "type": {"inward": "is caused by", "outward": "causes"}, + } + ) + + assert to_links_dict(link) == { + "id": "20001", + "key": "TEST-3", + "summary": "Linked issue", + "status": "To Do", + "type": "causes", + "issue_type": "Task", + } + + +def test_to_links_dict_includes_inward_issue_type() -> None: + link = SimpleNamespace( + raw={ + "id": "20002", + "inwardIssue": { + "key": "TEST-4", + "fields": { + "issuetype": {"name": "Bug"}, + "status": {"name": "Done"}, + "summary": "Related issue", + }, + }, + "type": {"inward": "is caused by", "outward": "causes"}, + } + ) + + assert to_links_dict(link) == { + "id": "20002", + "key": "TEST-4", + "summary": "Related issue", + "status": "Done", + "type": "is caused by", + "issue_type": "Bug", + } + + +def test_to_links_dict_handles_missing_optional_fields() -> None: + link = SimpleNamespace( + raw={ + "id": "20003", + "outwardIssue": {"key": "TEST-5", "fields": {}}, + "type": {"outward": "relates to"}, + } + ) + + assert to_links_dict(link) == { + "id": "20003", + "key": "TEST-5", + "summary": None, + "status": None, + "type": "relates to", + "issue_type": None, + }