"""Transition operations for JIRA."""
import logging
from typing import Any, Dict, List, Optional
from jira.exceptions import JIRAError
from mcp_jira.client import JiraClient
logger = logging.getLogger(__name__)
class TransitionOperations:
"""Handles JIRA workflow transition operations."""
def __init__(self, client: JiraClient):
"""Initialize transition operations.
Args:
client: JiraClient instance
"""
self.client = client
def get_transitions(
self,
issue_key: str,
) -> List[Dict[str, Any]]:
"""Get available transitions for an issue.
Args:
issue_key: Issue key
Returns:
List of available transitions
Raises:
JIRAError: If retrieval fails
"""
try:
jira = self.client.jira
logger.info(f"Getting transitions for issue {issue_key}")
transitions = jira.transitions(issue_key)
return [
{
"id": trans["id"],
"name": trans["name"],
"to_status": trans.get("to", {}).get("name"),
"has_screen": trans.get("hasScreen", False),
"is_global": trans.get("isGlobal", False),
"fields": trans.get("fields", {}),
}
for trans in transitions
]
except JIRAError as e:
logger.error(f"Failed to get transitions for {issue_key}: {e}")
raise
def transition_issue(
self,
issue_key: str,
transition: str,
fields: Optional[Dict[str, Any]] = None,
custom_fields: Optional[Dict[str, Any]] = None,
comment: Optional[str] = None,
) -> Dict[str, Any]:
"""Execute a workflow transition on an issue.
Args:
issue_key: Issue key
transition: Transition ID or name
fields: Dictionary of fields to set during transition
custom_fields: Dictionary of custom fields to set
comment: Optional comment to add with transition
Returns:
Dictionary with transition result
Raises:
JIRAError: If transition fails
"""
try:
jira = self.client.jira
# Prepare fields
transition_fields = fields or {}
# Add custom fields
if custom_fields:
prepared_custom = self.client.custom_fields.prepare_custom_fields(custom_fields)
transition_fields.update(prepared_custom)
# Add comment if provided
if comment:
transition_fields["comment"] = [{"add": {"body": comment}}]
logger.info(f"Transitioning issue {issue_key} to {transition}")
jira.transition_issue(
issue_key,
transition,
fields=transition_fields if transition_fields else None,
)
# Get the updated issue to return new status
issue = jira.issue(issue_key)
new_status = issue.fields.status.name if hasattr(issue.fields, "status") else None
return {
"key": issue_key,
"transitioned": True,
"new_status": new_status,
"message": f"Successfully transitioned issue {issue_key}",
}
except JIRAError as e:
logger.error(f"Failed to transition issue {issue_key}: {e}")
raise
def validate_transition(
self,
issue_key: str,
transition_name: str,
) -> Dict[str, Any]:
"""Check if a transition is available for an issue.
Args:
issue_key: Issue key
transition_name: Transition name to validate
Returns:
Dictionary with validation result
Raises:
JIRAError: If retrieval fails
"""
try:
transitions = self.get_transitions(issue_key)
available = any(
trans["name"].lower() == transition_name.lower()
for trans in transitions
)
matching_trans = None
if available:
matching_trans = next(
trans for trans in transitions
if trans["name"].lower() == transition_name.lower()
)
return {
"issue_key": issue_key,
"transition_name": transition_name,
"available": available,
"transition": matching_trans,
"all_transitions": [t["name"] for t in transitions],
}
except JIRAError as e:
logger.error(f"Failed to validate transition for {issue_key}: {e}")
raise