mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-22 00:30:19 +02:00
Filter workspace routines by project in API
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
462ea100e4
commit
e55b161d56
7 files changed
+71
-7
No files matched your search
@@ -145,6 +145,7 @@ describe("routine routes", () => {
|
||||
registerModuleMocks();
|
||||
vi.clearAllMocks();
|
||||
mockGetTelemetryClient.mockReturnValue({ track: vi.fn() });
|
||||
mockRoutineService.list.mockResolvedValue([routine]);
|
||||
mockRoutineService.create.mockResolvedValue(routine);
|
||||
mockRoutineService.get.mockResolvedValue(routine);
|
||||
mockRoutineService.getTrigger.mockResolvedValue(trigger);
|
||||
@@ -158,6 +159,23 @@ describe("routine routes", () => {
|
||||
mockLogActivity.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it("passes project filters to the routine list service", async () => {
|
||||
const app = await createApp({
|
||||
type: "board",
|
||||
userId: "board-user",
|
||||
source: "session",
|
||||
isInstanceAdmin: true,
|
||||
companyIds: [companyId],
|
||||
});
|
||||
|
||||
const res = await request(app)
|
||||
.get(`/api/companies/${companyId}/routines`)
|
||||
.query({ projectId });
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
expect(mockRoutineService.list).toHaveBeenCalledWith(companyId, { projectId });
|
||||
});
|
||||
|
||||
it("requires tasks:assign permission for non-admin board routine creation", async () => {
|
||||
const app = await createApp({
|
||||
type: "board",
|
||||
|
||||
@@ -178,6 +178,39 @@ describeEmbeddedPostgres("routine service live-execution coalescing", () => {
|
||||
return { companyId, agentId, issueSvc, projectId, routine, svc, wakeups };
|
||||
}
|
||||
|
||||
it("filters listed routines by project", async () => {
|
||||
const { companyId, agentId, projectId, routine, svc } = await seedFixture();
|
||||
const otherProjectId = randomUUID();
|
||||
await db.insert(projects).values({
|
||||
id: otherProjectId,
|
||||
companyId,
|
||||
name: "Other routines",
|
||||
status: "in_progress",
|
||||
});
|
||||
const otherRoutine = await svc.create(
|
||||
companyId,
|
||||
{
|
||||
projectId: otherProjectId,
|
||||
goalId: null,
|
||||
parentIssueId: null,
|
||||
title: "other project routine",
|
||||
description: null,
|
||||
assigneeAgentId: agentId,
|
||||
priority: "medium",
|
||||
status: "active",
|
||||
concurrencyPolicy: "coalesce_if_active",
|
||||
catchUpPolicy: "skip_missed",
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const projectRoutines = await svc.list(companyId, { projectId });
|
||||
const allRoutines = await svc.list(companyId);
|
||||
|
||||
expect(projectRoutines.map((entry) => entry.id)).toEqual([routine.id]);
|
||||
expect(allRoutines.map((entry) => entry.id)).toEqual(expect.arrayContaining([routine.id, otherRoutine.id]));
|
||||
});
|
||||
|
||||
it("creates a fresh execution issue when the previous routine issue is open but idle", async () => {
|
||||
const { companyId, issueSvc, routine, svc } = await seedFixture();
|
||||
const previousRunId = randomUUID();
|
||||
|
||||
@@ -60,7 +60,8 @@ export function routineRoutes(
|
||||
router.get("/companies/:companyId/routines", async (req, res) => {
|
||||
const companyId = req.params.companyId as string;
|
||||
assertCompanyAccess(req, companyId);
|
||||
const result = await svc.list(companyId);
|
||||
const projectId = typeof req.query.projectId === "string" ? req.query.projectId : undefined;
|
||||
const result = await svc.list(companyId, { projectId });
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
|
||||
@@ -1071,11 +1071,17 @@ export function routineService(
|
||||
get: getRoutineById,
|
||||
getTrigger: getTriggerById,
|
||||
|
||||
list: async (companyId: string): Promise<RoutineListItem[]> => {
|
||||
list: async (
|
||||
companyId: string,
|
||||
filters?: { projectId?: string | null },
|
||||
): Promise<RoutineListItem[]> => {
|
||||
const conditions = [eq(routines.companyId, companyId)];
|
||||
if (filters?.projectId) conditions.push(eq(routines.projectId, filters.projectId));
|
||||
|
||||
const rows = await db
|
||||
.select()
|
||||
.from(routines)
|
||||
.where(eq(routines.companyId, companyId))
|
||||
.where(and(...conditions))
|
||||
.orderBy(desc(routines.updatedAt), asc(routines.title));
|
||||
const routineIds = rows.map((row) => row.id);
|
||||
const [triggersByRoutine, latestRunByRoutine, activeIssueByRoutine] = await Promise.all([
|
||||
|
||||
@@ -22,7 +22,12 @@ export interface RotateRoutineTriggerResponse {
|
||||
}
|
||||
|
||||
export const routinesApi = {
|
||||
list: (companyId: string) => api.get<RoutineListItem[]>(`/companies/${companyId}/routines`),
|
||||
list: (companyId: string, filters?: { projectId?: string | null }) => {
|
||||
const params = new URLSearchParams();
|
||||
if (filters?.projectId) params.set("projectId", filters.projectId);
|
||||
const query = params.toString();
|
||||
return api.get<RoutineListItem[]>(`/companies/${companyId}/routines${query ? `?${query}` : ""}`);
|
||||
},
|
||||
create: (companyId: string, data: Record<string, unknown>) =>
|
||||
api.post<Routine>(`/companies/${companyId}/routines`, data),
|
||||
get: (id: string) => api.get<RoutineDetail>(`/routines/${id}`),
|
||||
|
||||
@@ -64,7 +64,8 @@ export const queryKeys = {
|
||||
workProducts: (issueId: string) => ["issues", "work-products", issueId] as const,
|
||||
},
|
||||
routines: {
|
||||
list: (companyId: string) => ["routines", companyId] as const,
|
||||
list: (companyId: string, filters?: { projectId?: string | null }) =>
|
||||
["routines", companyId, filters?.projectId ?? "__all-projects__"] as const,
|
||||
detail: (id: string) => ["routines", "detail", id] as const,
|
||||
runs: (id: string) => ["routines", "runs", id] as const,
|
||||
activity: (companyId: string, id: string) => ["routines", "activity", companyId, id] as const,
|
||||
|
||||
@@ -384,8 +384,8 @@ function ExecutionWorkspaceRoutinesList({
|
||||
const [runningRoutineId, setRunningRoutineId] = useState<string | null>(null);
|
||||
|
||||
const { data: routines, isLoading, error } = useQuery({
|
||||
queryKey: queryKeys.routines.list(workspace.companyId),
|
||||
queryFn: () => routinesApi.list(workspace.companyId),
|
||||
queryKey: queryKeys.routines.list(workspace.companyId, { projectId: workspace.projectId }),
|
||||
queryFn: () => routinesApi.list(workspace.companyId, { projectId: workspace.projectId }),
|
||||
});
|
||||
|
||||
const { data: agents } = useQuery({
|
||||
|
||||
Reference in new issue
Block a user