feat: enhance Azure DevOps integration by adding work item as a ticket retrieval methods - Supporting ticket context for Azure DevOps

Signed-off-by: abishlal <abishlalns03@gmail.com>
This commit is contained in:
abishlal
2025-06-21 17:20:31 +05:30
parent 37ef4bad8f
commit d8fea6afc4
3 changed files with 84 additions and 3 deletions

View File

@ -39,7 +39,7 @@ class AzureDevopsProvider(GitProvider):
"Azure DevOps provider is not available. Please install the required dependencies."
)
self.azure_devops_client = self._get_azure_devops_client()
self.azure_devops_client, self.azure_devops_board_client = self._get_azure_devops_client()
self.diff_files = None
self.workspace_slug = None
self.repo_slug = None
@ -593,8 +593,9 @@ class AzureDevopsProvider(GitProvider):
credentials = BasicAuthentication("", auth_token)
azure_devops_connection = Connection(base_url=org, creds=credentials)
azure_devops_client = azure_devops_connection.clients.get_git_client()
azure_devops_board_client = azure_devops_connection.clients.get_work_item_tracking_client()
return azure_devops_client
return azure_devops_client, azure_devops_board_client
def _get_repo(self):
if self.repo is None:
@ -636,3 +637,49 @@ class AzureDevopsProvider(GitProvider):
url = self.azure_devops_client.normalized_url + "/" + self.workspace_slug + "/_git/" + self.repo_slug + "/commit/" + last.commit_id
return url
def get_linked_work_items(self) -> list:
"""
Get linked work items from the PR.
"""
try:
work_items = self.azure_devops_client.get_pull_request_work_item_refs(
project=self.workspace_slug,
repository_id=self.repo_slug,
pull_request_id=self.pr_num,
)
ids = [work_item.id for work_item in work_items]
if not work_items:
return []
items = self.get_work_items(ids)
return items
except Exception as e:
get_logger().exception(f"Failed to get linked work items, error: {e}")
return []
def get_work_items(self, work_item_ids: int) -> list:
"""
Get work items by their IDs.
"""
try:
raw_work_items = self.azure_devops_board_client.get_work_items(
project=self.workspace_slug,
ids=work_item_ids,
)
work_items = []
for item in raw_work_items:
work_items.append(
{
"id": item.id,
"title": item.fields.get("System.Title", ""),
"url": item.url,
"body": item.fields.get("System.Description", ""),
"state": item.fields.get("System.State", ""),
"acceptance_criteria": item.fields.get(
"Microsoft.VSTS.Common.AcceptanceCriteria", ""
),
}
)
return work_items
except Exception as e:
get_logger().exception(f"Failed to get work items, error: {e}")
return []

View File

@ -199,6 +199,13 @@ Ticket Description:
{{ ticket.body }}
#####
{%- endif %}
{%- if ticket.requirements %}
Ticket Requirements:
#####
{{ ticket.requirements }}
#####
{%- endif %}
=====
{% endfor %}
{%- endif %}

View File

@ -3,6 +3,7 @@ import traceback
from pr_agent.config_loader import get_settings
from pr_agent.git_providers import GithubProvider
from pr_agent.git_providers import AzureDevopsProvider
from pr_agent.log import get_logger
# Compile the regex pattern once, outside the function
@ -131,6 +132,32 @@ async def extract_tickets(git_provider):
return tickets_content
elif isinstance(git_provider, AzureDevopsProvider):
tickets_info = git_provider.get_linked_work_items()
tickets_content = []
for ticket in tickets_info:
try:
ticket_body_str = ticket.get("body", "")
if len(ticket_body_str) > MAX_TICKET_CHARACTERS:
ticket_body_str = ticket_body_str[:MAX_TICKET_CHARACTERS] + "..."
tickets_content.append(
{
"ticket_id": ticket.get("id"),
"ticket_url": ticket.get("url"),
"title": ticket.get("title"),
"body": ticket_body_str,
"labels": ", ".join(ticket.get("labels", [])),
"requirements": ticket.get("acceptance_criteria", ""),
}
)
except Exception as e:
get_logger().error(
f"Error processing Azure DevOps ticket: {e}",
artifact={"traceback": traceback.format_exc()},
)
return tickets_content
except Exception as e:
get_logger().error(f"Error extracting tickets error= {e}",
artifact={"traceback": traceback.format_exc()})