mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-25 21:36:52 +08:00
130 lines
4.7 KiB
Python
130 lines
4.7 KiB
Python
"""Work item operations for Azure DevOps integration."""
|
|
|
|
from datetime import datetime
|
|
|
|
from openhands.core.logger import openhands_logger as logger
|
|
from openhands.integrations.azure_devops.service.base import AzureDevOpsMixinBase
|
|
from openhands.integrations.service_types import Comment, RequestMethod
|
|
|
|
|
|
class AzureDevOpsWorkItemsMixin(AzureDevOpsMixinBase):
|
|
"""Mixin for Azure DevOps work item operations.
|
|
|
|
Work Items are unique to Azure DevOps and represent tasks, bugs, user stories, etc.
|
|
in Azure Boards. This mixin provides methods to interact with work item comments.
|
|
"""
|
|
|
|
def _truncate_comment(self, comment: str, max_length: int = 1000) -> str:
|
|
"""Truncate comment to max length."""
|
|
if len(comment) <= max_length:
|
|
return comment
|
|
return comment[:max_length] + '...'
|
|
|
|
async def add_work_item_comment(
|
|
self, repository: str, work_item_id: int, comment_text: str
|
|
) -> dict:
|
|
"""Add a comment to an Azure DevOps work item.
|
|
|
|
API Reference: https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/comments/add-comment
|
|
|
|
Args:
|
|
repository: Repository name in format "organization/project/repo" (project extracted)
|
|
work_item_id: The work item ID
|
|
comment_text: The comment text to post
|
|
|
|
Returns:
|
|
API response with created comment information
|
|
|
|
Raises:
|
|
HTTPException: If the API request fails
|
|
"""
|
|
org, project, _ = self._parse_repository(repository)
|
|
|
|
# URL-encode components to handle spaces and special characters
|
|
org_enc = self._encode_url_component(org)
|
|
project_enc = self._encode_url_component(project)
|
|
|
|
url = f'{self.base_url}/{org_enc}/{project_enc}/_apis/wit/workItems/{work_item_id}/comments?api-version=7.1-preview.4'
|
|
|
|
payload = {
|
|
'text': comment_text,
|
|
}
|
|
|
|
response, _ = await self._make_request(
|
|
url=url, params=payload, method=RequestMethod.POST
|
|
)
|
|
|
|
logger.info(f'Added comment to work item {work_item_id} in project {project}')
|
|
return response
|
|
|
|
async def get_work_item_comments(
|
|
self, repository: str, work_item_id: int, max_comments: int = 100
|
|
) -> list[Comment]:
|
|
"""Get all comments from a work item.
|
|
|
|
API Reference: https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/comments/get-comments
|
|
|
|
Args:
|
|
repository: Repository name in format "organization/project/repo" (project extracted)
|
|
work_item_id: The work item ID
|
|
max_comments: Maximum number of comments to return
|
|
|
|
Returns:
|
|
List of Comment objects sorted by creation date
|
|
"""
|
|
org, project, _ = self._parse_repository(repository)
|
|
|
|
# URL-encode components to handle spaces and special characters
|
|
org_enc = self._encode_url_component(org)
|
|
project_enc = self._encode_url_component(project)
|
|
|
|
url = f'{self.base_url}/{org_enc}/{project_enc}/_apis/wit/workItems/{work_item_id}/comments?api-version=7.1-preview.4'
|
|
|
|
response, _ = await self._make_request(url)
|
|
|
|
comments_data = response.get('comments', [])
|
|
all_comments: list[Comment] = []
|
|
|
|
for comment_data in comments_data:
|
|
# Extract author information
|
|
author_info = comment_data.get('createdBy', {})
|
|
author = author_info.get('displayName', 'unknown')
|
|
|
|
# Parse dates
|
|
created_at = (
|
|
datetime.fromisoformat(
|
|
comment_data.get('createdDate', '').replace('Z', '+00:00')
|
|
)
|
|
if comment_data.get('createdDate')
|
|
else datetime.fromtimestamp(0)
|
|
)
|
|
|
|
modified_at = (
|
|
datetime.fromisoformat(
|
|
comment_data.get('modifiedDate', '').replace('Z', '+00:00')
|
|
)
|
|
if comment_data.get('modifiedDate')
|
|
else created_at
|
|
)
|
|
|
|
comment = Comment(
|
|
id=str(comment_data.get('id', 0)),
|
|
body=self._truncate_comment(comment_data.get('text', '')),
|
|
author=author,
|
|
created_at=created_at,
|
|
updated_at=modified_at,
|
|
system=False,
|
|
)
|
|
|
|
all_comments.append(comment)
|
|
|
|
# Sort by creation date and limit
|
|
all_comments.sort(key=lambda c: c.created_at)
|
|
return all_comments[:max_comments]
|
|
|
|
async def add_work_item_reaction(
|
|
self, repository: str, work_item_id: int, reaction_type: str = ':thumbsup:'
|
|
) -> dict:
|
|
comment_text = f'{reaction_type} OpenHands is processing this work item...'
|
|
return await self.add_work_item_comment(repository, work_item_id, comment_text)
|