Skip to content

Commit

Permalink
Fix Asignee and Reporter metadata for Epics in JiraReader (#16842)
Browse files Browse the repository at this point in the history
  • Loading branch information
Javtor authored Nov 6, 2024
1 parent 56358e5 commit 5b581a3
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# CHANGELOG

## [0.3.2] - 2024-11-05

- Add assignee and reporter emails as metadata
- Fix getting assignee and reporter for epics
- Add include_epics parameter

## [0.1.2] - 2024-02-13

- Add maintainers and keywords from library.json (llamahub)
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class JiraReader(BaseReader):
}
"""

include_epics: bool = True

def __init__(
self,
email: Optional[str] = None,
Expand All @@ -47,6 +49,7 @@ def __init__(
BasicAuth: Optional[BasicAuth] = None,
Oauth2: Optional[Oauth2] = None,
PATauth: Optional[PATauth] = None,
include_epics: bool = True,
) -> None:
from jira import JIRA

Expand Down Expand Up @@ -75,6 +78,8 @@ def __init__(
server=f"https://{BasicAuth['server_url']}",
)

self.include_epics = include_epics

def load_data(
self, query: str, start_at: int = 0, max_results: int = 50
) -> List[Document]:
Expand All @@ -91,14 +96,26 @@ def load_data(
epic_descripton = ""

for issue in relevant_issues:
# Iterates through only issues and not epics
if "parent" in (issue.raw["fields"]):
if issue.fields.assignee:
assignee = issue.fields.assignee.displayName

if issue.fields.reporter:
reporter = issue.fields.reporter.displayName

issue_type = issue.fields.issuetype.name
if issue_type == "Epic" and not self.include_epics:
continue

assignee = ""
assignee_email = ""
reporter = ""
reporter_email = ""
epic_key = ""
epic_summary = ""
epic_descripton = ""

if issue.fields.assignee:
assignee = issue.fields.assignee.displayName
assignee_email = issue.fields.assignee.emailAddress
if issue.fields.reporter:
reporter = issue.fields.reporter.displayName
reporter_email = issue.fields.reporter.emailAddress

if "parent" in issue.raw["fields"]:
if issue.raw["fields"]["parent"]["key"]:
epic_key = issue.raw["fields"]["parent"]["key"]

Expand All @@ -123,9 +140,11 @@ def load_data(
"labels": issue.fields.labels,
"status": issue.fields.status.name,
"assignee": assignee,
"assignee_email": assignee_email,
"reporter": reporter,
"reporter_email": reporter_email,
"project": issue.fields.project.name,
"issue_type": issue.fields.issuetype.name,
"issue_type": issue_type,
"priority": issue.fields.priority.name,
"epic_key": epic_key,
"epic_summary": epic_summary,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ license = "MIT"
maintainers = ["bearguy"]
name = "llama-index-readers-jira"
readme = "README.md"
version = "0.3.1"
version = "0.3.2"

[tool.poetry.dependencies]
python = ">=3.8.1,<4.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
python_tests()
python_tests(
dependencies=["llama-index-integrations/readers/llama-index-readers-jira:poetry"],
)
Original file line number Diff line number Diff line change
@@ -1,7 +1,173 @@
from llama_index.core.readers.base import BaseReader
from llama_index.readers.jira import JiraReader
import pytest
from unittest.mock import patch, MagicMock


def test_class():
names_of_base_classes = [b.__name__ for b in JiraReader.__mro__]
assert BaseReader.__name__ in names_of_base_classes


@pytest.fixture(autouse=True)
def mock_jira():
with patch("jira.JIRA") as mock_jira:
mock_jira.return_value = MagicMock()
yield mock_jira


@pytest.fixture()
def mock_issue():
issue = MagicMock()
# Setup basic issue fields
issue.id = "TEST-123"
issue.fields.summary = "Test Summary"
issue.fields.description = "Test Description"
issue.fields.issuetype.name = "Story"
issue.fields.created = "2024-01-01"
issue.fields.updated = "2024-01-02"
issue.fields.labels = ["test-label"]
issue.fields.status.name = "In Progress"
issue.fields.project.name = "Test Project"
issue.fields.priority.name = "High"

# Setup assignee and reporter
issue.fields.assignee = MagicMock()
issue.fields.assignee.displayName = "Test Assignee"
issue.fields.assignee.emailAddress = "[email protected]"
issue.fields.reporter = MagicMock()
issue.fields.reporter.displayName = "Test Reporter"
issue.fields.reporter.emailAddress = "[email protected]"

# Setup raw fields for parent/epic info
issue.raw = {
"fields": {
"parent": {
"key": "EPIC-1",
"fields": {
"summary": "Epic Summary",
"status": {"description": "Epic Description"},
},
}
}
}

issue.permalink.return_value = "https://test.atlassian.net/browse/TEST-123"
return issue


def test_basic_auth(mock_jira):
reader = JiraReader(
email="[email protected]",
api_token="test-token",
server_url="example.atlassian.net",
)

mock_jira.assert_called_once_with(
basic_auth=("[email protected]", "test-token"),
server="https://example.atlassian.net",
)


def test_oauth2(mock_jira):
reader = JiraReader(Oauth2={"cloud_id": "test-cloud", "api_token": "test-token"})

mock_jira.assert_called_once_with(
options={
"server": "https://api.atlassian.com/ex/jira/test-cloud",
"headers": {"Authorization": "Bearer test-token"},
}
)


def test_pat_auth(mock_jira):
reader = JiraReader(
PATauth={
"server_url": "https://example.atlassian.net",
"api_token": "test-token",
}
)

mock_jira.assert_called_once_with(
options={
"server": "https://example.atlassian.net",
"headers": {"Authorization": "Bearer test-token"},
}
)


def test_load_data_basic(mock_jira, mock_issue):
# Setup mock JIRA instance
jira_instance = mock_jira.return_value
jira_instance.search_issues.return_value = [mock_issue]

reader = JiraReader(
email="[email protected]",
api_token="test-token",
server_url="example.atlassian.net",
)

documents = reader.load_data("project = TEST")

# Verify search_issues was called correctly
jira_instance.search_issues.assert_called_once_with(
"project = TEST", startAt=0, maxResults=50
)

# Verify returned document
assert len(documents) == 1
doc = documents[0]
assert doc.doc_id == "TEST-123"
assert doc.text == "Test Summary \n Test Description"

# Verify extra_info
assert doc.extra_info["assignee"] == "Test Assignee"
assert doc.extra_info["assignee_email"] == "[email protected]"
assert doc.extra_info["reporter"] == "Test Reporter"
assert doc.extra_info["reporter_email"] == "[email protected]"
assert doc.extra_info["epic_key"] == "EPIC-1"
assert doc.extra_info["epic_summary"] == "Epic Summary"
assert doc.extra_info["epic_description"] == "Epic Description"


def test_load_data_exclude_epics(mock_jira, mock_issue):
# Modify mock issue to be an epic
mock_issue.fields.issuetype.name = "Epic"
jira_instance = mock_jira.return_value
jira_instance.search_issues.return_value = [mock_issue]

reader = JiraReader(
email="[email protected]",
api_token="test-token",
server_url="example.atlassian.net",
include_epics=False,
)

documents = reader.load_data("project = TEST")

# Verify no documents returned since epics are excluded
assert len(documents) == 0


def test_load_data_no_assignee_reporter(mock_jira, mock_issue):
# Remove assignee and reporter
mock_issue.fields.assignee = None
mock_issue.fields.reporter = None
jira_instance = mock_jira.return_value
jira_instance.search_issues.return_value = [mock_issue]

reader = JiraReader(
email="[email protected]",
api_token="test-token",
server_url="example.atlassian.net",
)

documents = reader.load_data("project = TEST")

# Verify document has empty assignee/reporter fields
assert len(documents) == 1
doc = documents[0]
assert doc.extra_info["assignee"] == ""
assert doc.extra_info["assignee_email"] == ""
assert doc.extra_info["reporter"] == ""
assert doc.extra_info["reporter_email"] == ""

0 comments on commit 5b581a3

Please sign in to comment.