refactor: Factor out Gitea interfacing into clean facade pattern
- Create new gitea/ package with clean API facade - Establish proper separation of concerns: tddai uses gitea, not vice versa - Replace duplicate curl+subprocess patterns with unified HTTP client - Add rich domain models with properties (issue.priority, issue.status) - Maintain full backwards compatibility in tddai modules - Reduce code complexity: -373 lines, +151 lines (net -222 lines) - Improve testability and maintainability through clean interfaces Architecture: - gitea.client.GiteaClient - main facade with sub-clients - gitea.api_client - high-level API with model conversion - gitea.http_client - low-level HTTP operations - gitea.models - rich domain objects (Issue, Milestone, Label) - gitea.config - gitea-specific configuration - gitea.exceptions - clean exception hierarchy 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,24 +1,31 @@
|
||||
"""
|
||||
Issue creation for Gitea API.
|
||||
Issue creation using the Gitea facade.
|
||||
|
||||
This module now acts as an adapter to the new gitea package,
|
||||
maintaining backwards compatibility while using the cleaner API.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
from subprocess import PIPE
|
||||
from typing import Dict, Any, Optional, List
|
||||
|
||||
from gitea import GiteaClient, GiteaConfig, Priority
|
||||
from .config import get_config
|
||||
from .exceptions import IssueError
|
||||
|
||||
|
||||
class IssueCreator:
|
||||
"""Creates new issues via Gitea API."""
|
||||
"""Creates new issues using the Gitea facade."""
|
||||
|
||||
def __init__(self, config=None, auth_token=None):
|
||||
self.config = config or get_config()
|
||||
self.auth_token = auth_token or os.getenv('GITEA_API_TOKEN')
|
||||
|
||||
# Create Gitea client from tddai config
|
||||
gitea_config = GiteaConfig.from_tddai_config(self.config)
|
||||
if self.auth_token:
|
||||
gitea_config.auth_token = self.auth_token
|
||||
self.gitea_client = GiteaClient(gitea_config)
|
||||
|
||||
def create_issue(self, title: str, body: str, **kwargs) -> Dict[str, Any]:
|
||||
"""Create a new issue via POST operation.
|
||||
|
||||
@@ -33,63 +40,30 @@ class IssueCreator:
|
||||
Raises:
|
||||
IssueError: If creation fails
|
||||
"""
|
||||
if not self.auth_token:
|
||||
raise IssueError("Authentication token required for issue creation")
|
||||
|
||||
if not title.strip():
|
||||
raise IssueError("Issue title cannot be empty")
|
||||
|
||||
# Prepare issue data
|
||||
issue_data = {
|
||||
'title': title.strip(),
|
||||
'body': body.strip() if body else ''
|
||||
}
|
||||
|
||||
# Add optional fields
|
||||
if 'assignees' in kwargs and kwargs['assignees']:
|
||||
issue_data['assignees'] = kwargs['assignees']
|
||||
|
||||
if 'milestone' in kwargs and kwargs['milestone']:
|
||||
issue_data['milestone'] = kwargs['milestone']
|
||||
|
||||
if 'labels' in kwargs and kwargs['labels']:
|
||||
issue_data['labels'] = kwargs['labels']
|
||||
|
||||
url = self.config.issues_api_url
|
||||
|
||||
try:
|
||||
# Prepare curl command with authentication
|
||||
curl_cmd = [
|
||||
'curl', '-s', '-X', 'POST',
|
||||
'-H', 'Content-Type: application/json',
|
||||
'-H', f'Authorization: token {self.auth_token}',
|
||||
'-d', json.dumps(issue_data),
|
||||
url
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
curl_cmd,
|
||||
stdout=PIPE,
|
||||
stderr=PIPE,
|
||||
universal_newlines=True,
|
||||
check=True
|
||||
issue = self.gitea_client.issues.create(
|
||||
title=title,
|
||||
body=body,
|
||||
assignees=kwargs.get('assignees', []),
|
||||
milestone=kwargs.get('milestone'),
|
||||
labels=kwargs.get('labels', [])
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
raise IssueError(f"Failed to create issue: {result.stderr}")
|
||||
# Convert back to dict format for backwards compatibility
|
||||
return {
|
||||
'number': issue.number,
|
||||
'title': issue.title,
|
||||
'body': issue.body,
|
||||
'state': issue.state,
|
||||
'html_url': issue.html_url,
|
||||
'created_at': issue.created_at.isoformat(),
|
||||
'updated_at': issue.updated_at.isoformat(),
|
||||
'assignee': {'login': issue.assignee.login} if issue.assignee else None,
|
||||
'labels': [{'name': label.name} for label in issue.labels]
|
||||
}
|
||||
|
||||
response_data = json.loads(result.stdout)
|
||||
|
||||
# Check for API error responses
|
||||
if 'message' in response_data and 'number' not in response_data:
|
||||
raise IssueError(f"Failed to create issue: {response_data['message']}")
|
||||
|
||||
return response_data
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
except Exception as e:
|
||||
raise IssueError(f"Failed to create issue: {e}")
|
||||
except json.JSONDecodeError as e:
|
||||
raise IssueError(f"Failed to parse response data: {e}")
|
||||
|
||||
def create_enhancement_issue(self, title: str, use_case: str,
|
||||
technical_requirements: str = "",
|
||||
|
||||
Reference in New Issue
Block a user