fix: document project access member endpoints

Add the private-project access-member routes to the OpenAPI registry and model their board-only and created-response behavior. Update shared route-test fixtures for the project read checks introduced by the privacy work.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-08-01 22:54:01 +00:00
1 parent a87df0c351
commit 7bf6cc9d85
3 files changed
+62 -4

No files matched your search

@@ -62,6 +62,12 @@ vi.mock("../services/index.js", () => ({
workspaceOperationService: () => ({}),
accessService: () => ({
canUser: vi.fn(),
decide: vi.fn(async (input: { action: string }) => ({
allowed: input.action === "project:read",
action: input.action,
reason: input.action === "project:read" ? "allow_explicit_grant" : "deny_missing_grant",
explanation: input.action === "project:read" ? "Allowed by test default." : "Missing permission.",
})),
hasPermission: vi.fn(),
}),
agentService: () => ({
@@ -222,7 +222,9 @@ function registerRouteMocks() {
issueThreadInteractionService: () => mockIssueThreadInteractionService,
taskWatchdogService: () => mockTaskWatchdogService,
logActivity: mockLogActivity,
projectService: () => ({}),
projectService: () => ({
getById: vi.fn(async (id: string) => ({ id, companyId })),
}),
routineService: () => ({
syncRunStatusForIssue: vi.fn(async () => undefined),
}),
@@ -387,14 +389,16 @@ describe("agent issue mutation checkout ownership", () => {
input.action === "issue:comment" ||
input.action === "issue:read" ||
input.action === "issue:mutate" ||
input.action === "company_scope:read",
input.action === "company_scope:read" ||
input.action === "project:read",
action: input.action,
reason:
input.action === "tasks:assign" ||
input.action === "issue:comment" ||
input.action === "issue:read" ||
input.action === "issue:mutate" ||
input.action === "company_scope:read"
input.action === "company_scope:read" ||
input.action === "project:read"
? "allow_explicit_grant"
: "deny_missing_grant",
explanation:
@@ -402,7 +406,8 @@ describe("agent issue mutation checkout ownership", () => {
input.action === "issue:comment" ||
input.action === "issue:read" ||
input.action === "issue:mutate" ||
input.action === "company_scope:read"
input.action === "company_scope:read" ||
input.action === "project:read"
? "Allowed by test default."
: "Missing permission.",
}));
+47
View File
@@ -39,6 +39,7 @@ import {
// Project
createProjectSchema,
updateProjectSchema,
addProjectAccessMemberSchema,
createProjectWorkspaceSchema,
updateProjectWorkspaceSchema,
// Company
@@ -776,6 +777,8 @@ const BOARD_ONLY_OPERATIONS = new Set([
"PUT /api/companies/{companyId}/resource-memberships/me/agents/{agentId}",
"PUT /api/companies/{companyId}/resource-memberships/me/documents/{documentId}",
"PUT /api/companies/{companyId}/resource-memberships/me/projects/{projectId}",
"POST /api/projects/{id}/access-members",
"DELETE /api/projects/{id}/access-members/{memberId}",
"GET /api/companies/{companyId}/secret-provider-configs",
"POST /api/companies/{companyId}/secret-provider-configs",
"GET /api/companies/{companyId}/secret-providers/health",
@@ -924,6 +927,7 @@ const CREATED_OPERATIONS = new Set([
"POST /api/issues/{id}/comments",
"POST /api/companies/{companyId}/issues/{issueId}/attachments",
"POST /api/companies/{companyId}/projects",
"POST /api/projects/{id}/access-members",
"POST /api/projects/{id}/workspaces",
"POST /api/companies/{companyId}/routines",
"POST /api/companies/{companyId}/folders",
@@ -2571,6 +2575,49 @@ registry.registerPath({
responses: { 200: r.ok(), 401: r.unauthorized },
});
registry.registerPath({
method: "get",
path: "/api/projects/{id}/access-members",
tags: ["projects"],
summary: "List project access members",
request: { params: z.object({ id: z.string() }) },
responses: { 200: r.ok(), 401: r.unauthorized, 403: r.forbidden, 404: r.notFound },
});
registry.registerPath({
method: "post",
path: "/api/projects/{id}/access-members",
tags: ["projects"],
summary: "Add a project access member",
request: {
params: z.object({ id: z.string() }),
body: jsonBody(addProjectAccessMemberSchema),
},
responses: {
201: r.ok(),
400: r.badRequest,
401: r.unauthorized,
403: r.forbidden,
404: r.notFound,
422: r.unprocessable,
},
});
registry.registerPath({
method: "delete",
path: "/api/projects/{id}/access-members/{memberId}",
tags: ["projects"],
summary: "Remove a project access member",
request: { params: z.object({ id: z.string(), memberId: z.string() }) },
responses: {
200: r.ok(),
401: r.unauthorized,
403: r.forbidden,
404: r.notFound,
422: r.unprocessable,
},
});
registry.registerPath({
method: "get",
path: "/api/projects/{id}/workspaces",