feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
"""API endpoints for Forgejo issue operations."""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from typing import TYPE_CHECKING
|
|
|
|
|
from uuid import UUID
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
2026-05-21 22:47:24 -05:00
|
|
|
from sqlalchemy import String, cast
|
|
|
|
|
from sqlmodel import func, select
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
|
2026-05-20 03:49:57 -05:00
|
|
|
from app.api.deps import require_org_member
|
2026-05-19 20:14:16 -05:00
|
|
|
from app.core.auth import AuthContext, get_auth_context
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
from app.db import crud
|
|
|
|
|
from app.db.session import get_session
|
2026-05-19 04:02:04 -05:00
|
|
|
from app.models.board_repository_links import BoardRepositoryLink
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
from app.models.forgejo_issues import ForgejoIssue
|
2026-05-21 22:47:24 -05:00
|
|
|
from app.schemas.forgejo_issues import (
|
|
|
|
|
CloseIssueResponse,
|
|
|
|
|
ForgejoIssueDetailRead,
|
|
|
|
|
ForgejoIssueListResponse,
|
|
|
|
|
ForgejoIssueRead,
|
|
|
|
|
)
|
2026-05-20 03:49:57 -05:00
|
|
|
from app.services.activity_log import record_activity
|
2026-05-21 22:47:24 -05:00
|
|
|
from app.services.forgejo_issue_close import (
|
|
|
|
|
CloseIssueAccessError,
|
|
|
|
|
CloseIssueNotFoundError,
|
|
|
|
|
CloseIssueRemoteError,
|
|
|
|
|
close_issue_by_id,
|
|
|
|
|
)
|
2026-05-20 03:49:57 -05:00
|
|
|
from app.services.organizations import OrganizationContext, list_accessible_board_ids
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/forgejo/issues", tags=["forgejo-issues"])
|
|
|
|
|
SESSION_DEP = Depends(get_session)
|
|
|
|
|
AUTH_DEP = Depends(get_auth_context)
|
2026-05-19 20:14:16 -05:00
|
|
|
ORG_MEMBER_DEP = Depends(require_org_member)
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("", response_model=ForgejoIssueListResponse)
|
|
|
|
|
async def list_issues(
|
|
|
|
|
session: AsyncSession = SESSION_DEP,
|
2026-05-19 20:14:16 -05:00
|
|
|
ctx: OrganizationContext = ORG_MEMBER_DEP,
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
repository_id: str | None = Query(None, description="Filter by repository ID"),
|
2026-05-21 22:47:24 -05:00
|
|
|
board_id: str | None = Query(
|
|
|
|
|
None, description="Filter by board ID (returns issues from all repos linked to this board)"
|
|
|
|
|
),
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
state: str | None = Query(None, description="Filter by state (open, closed)"),
|
|
|
|
|
label: str | None = Query(None, description="Filter by label name"),
|
|
|
|
|
assignee: str | None = Query(None, description="Filter by assignee login"),
|
|
|
|
|
search: str | None = Query(None, description="Search in title and body"),
|
|
|
|
|
page: int = Query(1, ge=1, description="Page number"),
|
|
|
|
|
limit: int = Query(30, ge=1, le=100, description="Items per page"),
|
|
|
|
|
) -> ForgejoIssueListResponse:
|
|
|
|
|
"""List cached issues with optional filters."""
|
|
|
|
|
# Build query with filters
|
2026-05-20 03:09:22 -05:00
|
|
|
statement = select(ForgejoIssue).where(
|
|
|
|
|
ForgejoIssue.organization_id == ctx.organization.id,
|
|
|
|
|
ForgejoIssue.is_pull_request.is_(False),
|
|
|
|
|
)
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
|
2026-05-20 03:27:14 -05:00
|
|
|
if board_id:
|
|
|
|
|
try:
|
|
|
|
|
board_uuid = UUID(board_id)
|
|
|
|
|
except ValueError:
|
2026-05-21 22:47:24 -05:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail="Invalid board_id format"
|
|
|
|
|
)
|
2026-05-20 03:27:14 -05:00
|
|
|
linked_repo_ids = (
|
|
|
|
|
await session.exec(
|
|
|
|
|
select(BoardRepositoryLink.repository_id).where(
|
|
|
|
|
BoardRepositoryLink.board_id == board_uuid,
|
|
|
|
|
BoardRepositoryLink.organization_id == ctx.organization.id,
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
).all()
|
|
|
|
|
if not linked_repo_ids:
|
|
|
|
|
return ForgejoIssueListResponse(items=[], total=0, page=page, limit=limit)
|
|
|
|
|
statement = statement.where(ForgejoIssue.repository_id.in_(linked_repo_ids))
|
|
|
|
|
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
if repository_id:
|
|
|
|
|
try:
|
|
|
|
|
repo_uuid = UUID(repository_id)
|
|
|
|
|
except ValueError:
|
2026-05-21 22:47:24 -05:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
|
|
|
detail="Invalid repository_id format",
|
|
|
|
|
)
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
statement = statement.where(ForgejoIssue.repository_id == repo_uuid)
|
|
|
|
|
|
|
|
|
|
if state:
|
|
|
|
|
statement = statement.where(ForgejoIssue.state == state)
|
|
|
|
|
|
2026-05-20 01:44:31 -05:00
|
|
|
if label:
|
|
|
|
|
# Filter by label name — search within the JSON labels array cast to text
|
2026-05-21 22:47:24 -05:00
|
|
|
statement = statement.where(cast(ForgejoIssue.labels, String).ilike(f"%{label}%"))
|
2026-05-20 01:44:31 -05:00
|
|
|
|
|
|
|
|
if assignee:
|
|
|
|
|
# Filter by assignee login — search within the JSON assignees array cast to text
|
2026-05-21 22:47:24 -05:00
|
|
|
statement = statement.where(cast(ForgejoIssue.assignees, String).ilike(f"%{assignee}%"))
|
2026-05-20 01:44:31 -05:00
|
|
|
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
if search:
|
|
|
|
|
statement = statement.where(
|
2026-05-21 22:47:24 -05:00
|
|
|
(ForgejoIssue.title.ilike(f"%{search}%"))
|
|
|
|
|
| (ForgejoIssue.body_preview.ilike(f"%{search}%"))
|
|
|
|
|
| (ForgejoIssue.body.ilike(f"%{search}%"))
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Count total
|
2026-05-21 22:47:24 -05:00
|
|
|
total_statement = (
|
|
|
|
|
select(func.count())
|
|
|
|
|
.select_from(ForgejoIssue)
|
|
|
|
|
.where(
|
|
|
|
|
ForgejoIssue.organization_id == ctx.organization.id,
|
|
|
|
|
ForgejoIssue.is_pull_request.is_(False),
|
|
|
|
|
)
|
2026-05-20 03:09:22 -05:00
|
|
|
)
|
2026-05-20 03:27:14 -05:00
|
|
|
if board_id:
|
|
|
|
|
try:
|
|
|
|
|
board_uuid = UUID(board_id)
|
|
|
|
|
linked_repo_ids_for_count = (
|
|
|
|
|
await session.exec(
|
|
|
|
|
select(BoardRepositoryLink.repository_id).where(
|
|
|
|
|
BoardRepositoryLink.board_id == board_uuid,
|
|
|
|
|
BoardRepositoryLink.organization_id == ctx.organization.id,
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
).all()
|
|
|
|
|
if linked_repo_ids_for_count:
|
|
|
|
|
total_statement = total_statement.where(
|
|
|
|
|
ForgejoIssue.repository_id.in_(linked_repo_ids_for_count)
|
|
|
|
|
)
|
|
|
|
|
except ValueError:
|
|
|
|
|
pass
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
if repository_id:
|
|
|
|
|
try:
|
|
|
|
|
repo_uuid = UUID(repository_id)
|
|
|
|
|
total_statement = total_statement.where(ForgejoIssue.repository_id == repo_uuid)
|
|
|
|
|
except ValueError:
|
|
|
|
|
pass
|
|
|
|
|
if state:
|
|
|
|
|
total_statement = total_statement.where(ForgejoIssue.state == state)
|
2026-05-20 01:44:31 -05:00
|
|
|
if label:
|
|
|
|
|
total_statement = total_statement.where(
|
|
|
|
|
cast(ForgejoIssue.labels, String).ilike(f"%{label}%")
|
|
|
|
|
)
|
|
|
|
|
if assignee:
|
|
|
|
|
total_statement = total_statement.where(
|
|
|
|
|
cast(ForgejoIssue.assignees, String).ilike(f"%{assignee}%")
|
|
|
|
|
)
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
if search:
|
|
|
|
|
total_statement = total_statement.where(
|
2026-05-21 22:47:24 -05:00
|
|
|
(ForgejoIssue.title.ilike(f"%{search}%"))
|
|
|
|
|
| (ForgejoIssue.body_preview.ilike(f"%{search}%"))
|
|
|
|
|
| (ForgejoIssue.body.ilike(f"%{search}%"))
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
)
|
|
|
|
|
total_result = await session.exec(total_statement)
|
|
|
|
|
total = total_result.one()
|
|
|
|
|
|
|
|
|
|
# Pagination
|
|
|
|
|
offset = (page - 1) * limit
|
2026-05-21 22:47:24 -05:00
|
|
|
statement = (
|
|
|
|
|
statement.offset(offset).limit(limit).order_by(ForgejoIssue.forgejo_issue_number.desc())
|
|
|
|
|
)
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
|
|
|
|
|
issues = (await session.exec(statement)).all()
|
|
|
|
|
items = [ForgejoIssueRead.model_validate(issue) for issue in issues]
|
|
|
|
|
|
|
|
|
|
return ForgejoIssueListResponse(
|
|
|
|
|
items=items,
|
|
|
|
|
total=total,
|
|
|
|
|
page=page,
|
|
|
|
|
limit=limit,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-05-21 22:47:24 -05:00
|
|
|
@router.get("/{issue_id}", response_model=ForgejoIssueDetailRead)
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
async def get_issue(
|
|
|
|
|
issue_id: str,
|
|
|
|
|
session: AsyncSession = SESSION_DEP,
|
2026-05-19 20:14:16 -05:00
|
|
|
ctx: OrganizationContext = ORG_MEMBER_DEP,
|
2026-05-21 22:47:24 -05:00
|
|
|
) -> ForgejoIssueDetailRead:
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
"""Get one cached issue by ID."""
|
|
|
|
|
try:
|
|
|
|
|
uuid = UUID(issue_id)
|
|
|
|
|
except ValueError:
|
2026-05-21 22:47:24 -05:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail="Invalid issue_id format"
|
|
|
|
|
)
|
feat(forgejo): add validation, cached issues, sync service, and human issue APIs (Issues 5-8); add connection and repository admin UI (Issues 3-4); fix migration graph and client bugs
Backend Issues 5-8:
- POST /forgejo/connections/{id}/validate and /repositories/{id}/validate
- ForgejoIssue model with unique constraint (repo_id, issue_number)
- IssueSyncService with pagination and upsert
- GET /forgejo/issues with filtering, search, pagination
- GET /forgejo/issues/{id} with org-scoped access
- Fixed ForgejoAPIClient /api/v1 path prefix
- Fixed get_forgejo_client async context (was async def, now regular def)
- Fixed Forgejo API response parsing (list, not dict with items)
- Fixed None-safe handling for labels/assignees
Frontend Issues 3-4:
- Connections list, new, edit pages with token masking
- Repositories list, new, edit pages with connection selector
- ForgejoConnectionForm, ForgejoConnectionsTable components
- ForgejoRepositoryForm, ForgejoRepositoriesTable components
- api-forgejo.ts client library
Migration cleanup:
- Consolidated forgejo_issues table into f5a2b3c4d5e6 migration
- Removed orphan branch migrations for already-existing tables
- Fixed migration graph to single head (f5a2b3c4d5e6)
- Stamped DB to correct revision
2026-05-19 03:10:32 -05:00
|
|
|
|
|
|
|
|
issue = await crud.get_by_id(session, ForgejoIssue, uuid)
|
|
|
|
|
if issue is None:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
if issue.organization_id != ctx.organization.id:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
|
2026-05-21 22:47:24 -05:00
|
|
|
return ForgejoIssueDetailRead.model_validate(issue)
|
2026-05-19 04:02:04 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post(
|
|
|
|
|
"/{issue_id}/close",
|
|
|
|
|
response_model=CloseIssueResponse,
|
|
|
|
|
summary="Close a Forgejo issue (human user)",
|
|
|
|
|
description=(
|
|
|
|
|
"Close a Forgejo issue by its local ID. The user must have write access "
|
|
|
|
|
"to the board that the issue's repository is linked to."
|
|
|
|
|
),
|
|
|
|
|
responses={
|
|
|
|
|
status.HTTP_200_OK: {
|
|
|
|
|
"description": "Issue closed successfully",
|
|
|
|
|
"content": {
|
|
|
|
|
"application/json": {
|
|
|
|
|
"example": {
|
|
|
|
|
"success": True,
|
|
|
|
|
"issue_id": "123e4567-e89b-12d3-a456-426614174000",
|
|
|
|
|
"forgejo_issue_number": 42,
|
|
|
|
|
"state": "closed",
|
|
|
|
|
"forgejo_closed_at": "2026-05-19T03:43:00+00:00",
|
|
|
|
|
"last_synced_at": "2026-05-19T03:43:00+00:00",
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
status.HTTP_404_NOT_FOUND: {
|
|
|
|
|
"description": "Issue not found or not linked to a board",
|
|
|
|
|
},
|
|
|
|
|
status.HTTP_403_FORBIDDEN: {
|
|
|
|
|
"description": "User lacks write access to the board",
|
|
|
|
|
},
|
|
|
|
|
status.HTTP_409_CONFLICT: {
|
|
|
|
|
"description": "Organization mismatch or access denied",
|
|
|
|
|
},
|
|
|
|
|
status.HTTP_502_BAD_GATEWAY: {
|
|
|
|
|
"description": "Forgejo API call failed",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
async def close_issue(
|
|
|
|
|
issue_id: str,
|
|
|
|
|
session: AsyncSession = SESSION_DEP,
|
2026-05-19 20:14:16 -05:00
|
|
|
auth: AuthContext = AUTH_DEP,
|
|
|
|
|
ctx: OrganizationContext = ORG_MEMBER_DEP,
|
2026-05-19 04:02:04 -05:00
|
|
|
) -> CloseIssueResponse:
|
|
|
|
|
"""Close a Forgejo issue as an authenticated user.
|
2026-05-21 22:47:24 -05:00
|
|
|
|
2026-05-19 04:02:04 -05:00
|
|
|
The user must have write access to the board that the issue's repository
|
|
|
|
|
is linked to. The issue must belong to a repository linked to that board.
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
uuid = UUID(issue_id)
|
|
|
|
|
except ValueError:
|
2026-05-21 22:47:24 -05:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT, detail="Invalid issue_id format"
|
|
|
|
|
)
|
2026-05-19 04:02:04 -05:00
|
|
|
|
|
|
|
|
issue = await crud.get_by_id(session, ForgejoIssue, uuid)
|
|
|
|
|
if issue is None:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Issue not found")
|
2026-05-20 03:49:57 -05:00
|
|
|
if issue.organization_id != ctx.organization.id:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Issue not found")
|
|
|
|
|
if auth.user is None:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
|
2026-05-19 04:02:04 -05:00
|
|
|
|
2026-05-20 03:49:57 -05:00
|
|
|
# Get boards linked to this issue's repository for this organization.
|
|
|
|
|
links = (
|
|
|
|
|
await session.exec(
|
|
|
|
|
select(BoardRepositoryLink).where(
|
|
|
|
|
BoardRepositoryLink.organization_id == ctx.organization.id,
|
|
|
|
|
BoardRepositoryLink.repository_id == issue.repository_id,
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
).all()
|
|
|
|
|
if not links:
|
2026-05-19 04:02:04 -05:00
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
|
|
|
detail="Issue repository is not linked to any board",
|
|
|
|
|
)
|
|
|
|
|
|
2026-05-21 22:47:24 -05:00
|
|
|
allowed_board_ids = set(await list_accessible_board_ids(session, member=ctx.member, write=True))
|
2026-05-20 03:49:57 -05:00
|
|
|
authorized_board_id = next(
|
|
|
|
|
(link.board_id for link in links if link.board_id in allowed_board_ids),
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
if authorized_board_id is None:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
|
|
|
detail="Board access denied",
|
|
|
|
|
)
|
2026-05-19 04:02:04 -05:00
|
|
|
|
2026-05-20 03:49:57 -05:00
|
|
|
# Close the issue using the service.
|
2026-05-19 04:02:04 -05:00
|
|
|
try:
|
|
|
|
|
result = await close_issue_by_id(
|
|
|
|
|
session=session,
|
|
|
|
|
issue_id=uuid,
|
2026-05-19 20:14:16 -05:00
|
|
|
actor_user_id=auth.user.id,
|
2026-05-19 04:02:04 -05:00
|
|
|
)
|
|
|
|
|
except CloseIssueNotFoundError as e:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
|
|
|
|
except CloseIssueAccessError as e:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))
|
|
|
|
|
except CloseIssueRemoteError as e:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=str(e))
|
|
|
|
|
|
2026-05-20 03:49:57 -05:00
|
|
|
repository_full_name = str(result.get("repository_full_name") or "unknown/unknown")
|
|
|
|
|
record_activity(
|
|
|
|
|
session,
|
|
|
|
|
event_type="forgejo.issue.closed",
|
|
|
|
|
message=(
|
|
|
|
|
"Forgejo issue closed by user "
|
|
|
|
|
f"{auth.user.id}: {repository_full_name}#{result['forgejo_issue_number']}"
|
|
|
|
|
),
|
|
|
|
|
board_id=authorized_board_id,
|
|
|
|
|
)
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
2026-05-19 04:02:04 -05:00
|
|
|
return CloseIssueResponse(
|
|
|
|
|
success=result["success"],
|
|
|
|
|
issue_id=result["issue_id"],
|
|
|
|
|
forgejo_issue_number=result["forgejo_issue_number"],
|
|
|
|
|
state=result["state"],
|
|
|
|
|
forgejo_closed_at=result.get("forgejo_closed_at"),
|
|
|
|
|
last_synced_at=result.get("last_synced_at") or "",
|
|
|
|
|
)
|