mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-10 12:37:55 +01:00
fix(skills): resolve slug references in skillId route param (#19808)
The skillId route param handler called getById with the raw path segment. A slug such as inbox-triage then reached the uuid id column, Postgres rejected the cast, and the request returned 500. The handler now resolves the reference with getByRouteRef (UUID, unique slug, or key) and rewrites req.params.skillId to the skill's UUID. An unknown reference returns 404. Fixes PAPERCLIP-BACKEND-6H Co-Authored-By: Devin Foley <devinfoley@users.noreply.github.com>
This commit is contained in:
1 parent
2175b8db93
commit
a8ab1d3d94
2 files changed
+34
-1
No files matched your search
@@ -45,6 +45,7 @@ const mockCompanySkillService = vi.hoisted(() => ({
|
||||
deleteSkill: vi.fn(),
|
||||
auditSkill: vi.fn(),
|
||||
getById: vi.fn(),
|
||||
getByRouteRef: vi.fn(),
|
||||
installUpdate: vi.fn(),
|
||||
resetSkill: vi.fn(),
|
||||
listTestInputs: vi.fn(),
|
||||
@@ -314,6 +315,10 @@ describe("company skill mutation permissions", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockCompanySkillService.getByRouteRef.mockImplementation(async (companyId: string, ref: string) => {
|
||||
const skill = await mockCompanySkillService.getById(companyId, ref);
|
||||
return skill ? { ...skill, id: ref } : skill;
|
||||
});
|
||||
mockHeartbeatFactory.mockReturnValue(mockHeartbeatService);
|
||||
mockCaptureRunIdentity.mockResolvedValue({ run: { responsibleUserId: "validated-owner" } });
|
||||
mockSkillSourceService.sourceForSkill.mockResolvedValue(null);
|
||||
@@ -826,6 +831,32 @@ describe("company skill mutation permissions", () => {
|
||||
expect(mockCompanySkillService.scanProjectWorkspaces).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("resolves skill slugs in the skillId param to the canonical skill id", async () => {
|
||||
const skillId = "22222222-2222-4222-8222-222222222222";
|
||||
mockCompanySkillService.getByRouteRef.mockResolvedValue({ id: skillId, slug: "inbox-triage" });
|
||||
const app = await createApp(boardActor("board-user", "session"));
|
||||
|
||||
await request(app)
|
||||
.post("/api/companies/company-1/skills/inbox-triage/audit")
|
||||
.send({})
|
||||
.expect(200);
|
||||
|
||||
expect(mockCompanySkillService.getByRouteRef).toHaveBeenCalledWith("company-1", "inbox-triage");
|
||||
expect(mockCompanySkillService.auditSkill).toHaveBeenCalledWith("company-1", skillId);
|
||||
});
|
||||
|
||||
it("returns 404 when the skillId param does not match any skill", async () => {
|
||||
mockCompanySkillService.getByRouteRef.mockResolvedValue(null);
|
||||
const app = await createApp(boardActor("board-user", "session"));
|
||||
|
||||
await request(app)
|
||||
.post("/api/companies/company-1/skills/missing-skill/audit")
|
||||
.send({})
|
||||
.expect(404);
|
||||
|
||||
expect(mockCompanySkillService.auditSkill).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("allows board users with skills:create to create, import, install, update, delete, audit, and reset company skills", async () => {
|
||||
const app = await createApp(boardActor("board-user", "session"));
|
||||
|
||||
|
||||
@@ -109,7 +109,9 @@ export function companySkillRoutes(db: Db, options: { pluginWorkerManager?: Plug
|
||||
try {
|
||||
if (req.actor.type === "none") throw unauthorized("Authentication required");
|
||||
assertCompanyAccess(req, String(req.params.companyId));
|
||||
if (!await svc.getById(String(req.params.companyId), id)) throw notFound("Skill not found");
|
||||
const skill = await svc.getByRouteRef(String(req.params.companyId), id);
|
||||
if (!skill) throw notFound("Skill not found");
|
||||
req.params.skillId = skill.id;
|
||||
next();
|
||||
} catch (error) { next(error); }
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user