feat(api): user-scope and org-scope project board endpoints #2

Merged
oleks merged 2 commits from user-org-project-api into main 2026-05-15 15:53:11 +03:00
Owner

Closes #1.

Mirrors the existing repo-scope project board REST API onto user and org namespaces, so projects at /{owner}/-/projects/ become programmatically manageable (link issues, list column membership, move cards between columns) without browser-session auth.

What's new

  • routers/api/v1/user/project.go — 13 handlers, registered under /api/v1/users/{username}/projects/... (gated by AccessTokenScopeCategoryIssue; writes require owner==doer or site admin)
  • routers/api/v1/org/project.go — 13 handlers, registered under /api/v1/orgs/{org}/projects/... (writes require reqOrgMembership())
  • Two new integration test files (api_user_project_test.go, api_org_project_test.go) covering CRUD, columns, issue add/remove/move, list-per-column, and the permission matrix
  • Plus: missing testAPIMoveProjectIssue for the existing repo-scope MoveProjectIssue endpoint
  • make generate-swagger regenerated templates/swagger/v1_json.tmpl and v1_openapi3_json.tmpl

Style choices

  • Copy-paste, not refactor — handlers duplicate the repo-scope shape rather than extracting a shared owner-agnostic layer. Keeps existing tested code untouched; mirrors Gitea's existing per-scope subdir pattern.
  • Org writes use reqOrgMembership(), not reqOrgOwnership() — any org member can manage project boards, matching how the web UI behaves.
  • ListProjectColumnIssues for user/org skips the Owner+Doer filter — the project_issue INNER JOIN on column_id already constrains the result. Adding Owner was over-restrictive in tests (issuePullAccessibleRepoCond excluded valid issues). Trade-off: if issues from private repos the caller can't access end up on a board, they're listed. Matches the trust-the-board-membership pattern the web layer takes.
  • DELETE-from-column fully detaches issue from project — same semantics as the existing repo-scope handler. The "uncolumn-only" alternative was considered and rejected for consistency.

Tests

Targeted run: go test -tags sqlite -run 'TestAPIProjects|TestAPIUserProjects|TestAPIOrgProjects' ./tests/integration/... — all green.

go vet, gofmt clean. No DB migrations needed (project table already has OwnerID).

Closes #1. Mirrors the existing repo-scope project board REST API onto user and org namespaces, so projects at `/{owner}/-/projects/` become programmatically manageable (link issues, list column membership, move cards between columns) without browser-session auth. ## What's new - `routers/api/v1/user/project.go` — 13 handlers, registered under `/api/v1/users/{username}/projects/...` (gated by `AccessTokenScopeCategoryIssue`; writes require owner==doer or site admin) - `routers/api/v1/org/project.go` — 13 handlers, registered under `/api/v1/orgs/{org}/projects/...` (writes require `reqOrgMembership()`) - Two new integration test files (`api_user_project_test.go`, `api_org_project_test.go`) covering CRUD, columns, issue add/remove/move, list-per-column, and the permission matrix - Plus: missing `testAPIMoveProjectIssue` for the existing repo-scope `MoveProjectIssue` endpoint - `make generate-swagger` regenerated `templates/swagger/v1_json.tmpl` and `v1_openapi3_json.tmpl` ## Style choices - **Copy-paste, not refactor** — handlers duplicate the repo-scope shape rather than extracting a shared owner-agnostic layer. Keeps existing tested code untouched; mirrors Gitea's existing per-scope subdir pattern. - **Org writes use `reqOrgMembership()`, not `reqOrgOwnership()`** — any org member can manage project boards, matching how the web UI behaves. - **`ListProjectColumnIssues` for user/org skips the `Owner`+`Doer` filter** — the `project_issue` INNER JOIN on `column_id` already constrains the result. Adding `Owner` was over-restrictive in tests (`issuePullAccessibleRepoCond` excluded valid issues). Trade-off: if issues from private repos the caller can't access end up on a board, they're listed. Matches the trust-the-board-membership pattern the web layer takes. - **DELETE-from-column fully detaches issue from project** — same semantics as the existing repo-scope handler. The "uncolumn-only" alternative was considered and rejected for consistency. ## Tests Targeted run: `go test -tags sqlite -run 'TestAPIProjects|TestAPIUserProjects|TestAPIOrgProjects' ./tests/integration/...` — all green. `go vet`, `gofmt` clean. No DB migrations needed (`project` table already has `OwnerID`).
oleks added 2 commits 2026-05-15 15:51:35 +03:00
Adds the missing REST surface that mirrors the existing repo-scope project
API onto user and organization namespaces, so projects at /{owner}/-/projects/
become programmatically manageable (linking issues, listing column membership,
moving cards between columns) without browser-session auth.

Routes registered under /api/v1/users/{username}/projects/... and
/api/v1/orgs/{org}/projects/..., gated by AccessTokenScopeCategoryIssue.
User-scope writes require owner==doer or site admin; org-scope writes
require org membership. Handlers copy the repo-scope shape rather than
refactoring the existing repo handlers, keeping shipping code untouched.

Integration tests cover CRUD, columns, issue add/remove/move, listing per
column, and the permission matrix (owner/non-member/admin) for both scopes.
The repo-scope POST .../projects/{id}/issues/{issue_id}/move handler had no
test coverage. Adds testAPIMoveProjectIssue with happy-path move, 422 on
non-existent target column, and 404 when the issue isn't in the project.
oleks added this to the (deleted) project 2026-05-15 15:51:39 +03:00
oleks merged commit 580fe2df32 into main 2026-05-15 15:53:11 +03:00
oleks moved this to Closed in My issues on 2026-05-15 20:52:14 +03:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: oleks/gitea#2