fix: Add missing API endpoints and validation improvements
- Add cancel_sprint and delete_sprint endpoints to sprints.py - Add unassign_issue endpoint to issues.py - Add remove_issue_from_sprint endpoint to sprints.py - Add CRUD methods: remove_sprint_from_issues, unassign, remove_from_sprint - Add validation to prevent closed issues in active/planned sprints - Add authorization tests for SSE events endpoint - Fix IDOR vulnerabilities in agents.py and projects.py - Add Syndarix models migration (0004) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -432,6 +432,94 @@ class CRUDIssue(CRUDBase[Issue, IssueCreate, IssueUpdate]):
|
||||
logger.error(f"Error getting pending sync issues: {e!s}", exc_info=True)
|
||||
raise
|
||||
|
||||
async def remove_sprint_from_issues(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
*,
|
||||
sprint_id: UUID,
|
||||
) -> int:
|
||||
"""Remove sprint assignment from all issues in a sprint.
|
||||
|
||||
Used when deleting a sprint to clean up references.
|
||||
|
||||
Returns:
|
||||
Number of issues updated
|
||||
"""
|
||||
try:
|
||||
from sqlalchemy import update
|
||||
|
||||
result = await db.execute(
|
||||
update(Issue)
|
||||
.where(Issue.sprint_id == sprint_id)
|
||||
.values(sprint_id=None)
|
||||
)
|
||||
await db.commit()
|
||||
return result.rowcount
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(
|
||||
f"Error removing sprint {sprint_id} from issues: {e!s}",
|
||||
exc_info=True,
|
||||
)
|
||||
raise
|
||||
|
||||
async def unassign(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
*,
|
||||
issue_id: UUID,
|
||||
) -> Issue | None:
|
||||
"""Remove agent assignment from an issue.
|
||||
|
||||
Returns:
|
||||
Updated issue or None if not found
|
||||
"""
|
||||
try:
|
||||
result = await db.execute(select(Issue).where(Issue.id == issue_id))
|
||||
issue = result.scalar_one_or_none()
|
||||
|
||||
if not issue:
|
||||
return None
|
||||
|
||||
issue.assigned_agent_id = None
|
||||
await db.commit()
|
||||
await db.refresh(issue)
|
||||
return issue
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"Error unassigning issue {issue_id}: {e!s}", exc_info=True)
|
||||
raise
|
||||
|
||||
async def remove_from_sprint(
|
||||
self,
|
||||
db: AsyncSession,
|
||||
*,
|
||||
issue_id: UUID,
|
||||
) -> Issue | None:
|
||||
"""Remove an issue from its current sprint.
|
||||
|
||||
Returns:
|
||||
Updated issue or None if not found
|
||||
"""
|
||||
try:
|
||||
result = await db.execute(select(Issue).where(Issue.id == issue_id))
|
||||
issue = result.scalar_one_or_none()
|
||||
|
||||
if not issue:
|
||||
return None
|
||||
|
||||
issue.sprint_id = None
|
||||
await db.commit()
|
||||
await db.refresh(issue)
|
||||
return issue
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(
|
||||
f"Error removing issue {issue_id} from sprint: {e!s}",
|
||||
exc_info=True,
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
# Create a singleton instance for use across the application
|
||||
issue = CRUDIssue(Issue)
|
||||
|
||||
Reference in New Issue
Block a user