From 338e80fc33b6399816e0c74bc2d50e086834519f Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Mon, 6 Apr 2026 20:48:26 -0700 Subject: [PATCH] =?UTF-8?q?fix(intake):=20support=20comma-separated=20stat?= =?UTF-8?q?us=20filter=20in=20GET=20/intake/jobs=20=E2=80=94=20fixes=20mob?= =?UTF-8?q?ile=20polling=20contract=20mismatch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/modules/intake/repository.ts | 21 +++++++++++++++++---- backend/src/modules/intake/routes.ts | 8 ++++++-- backend/src/modules/intake/types.ts | 2 +- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/backend/src/modules/intake/repository.ts b/backend/src/modules/intake/repository.ts index 9e6a345..8ce1232 100644 --- a/backend/src/modules/intake/repository.ts +++ b/backend/src/modules/intake/repository.ts @@ -56,16 +56,29 @@ export async function getIntakeJob(id: string, userId: string): Promise { const filter: FilterMap = { userId, productId }; - if (options?.status) filter.status = options.status; - return jobsCollection().findMany({ + // If a single status is provided, use it as a direct filter for efficiency + if (options?.statuses && options.statuses.length === 1) { + filter.status = options.statuses[0]; + } + const limit = options?.limit ?? 20; + // Fetch more if we need to filter client-side for multiple statuses + const fetchLimit = options?.statuses && options.statuses.length > 1 ? Math.min(limit * 3, 100) : limit; + let jobs = await jobsCollection().findMany({ filter, sort: { startedAt: -1 }, - limit: options?.limit ?? 20, + limit: fetchLimit, offset: options?.offset ?? 0, }); + // Client-side filter for multiple statuses + if (options?.statuses && options.statuses.length > 1) { + const statusSet = new Set(options.statuses); + jobs = jobs.filter((j) => statusSet.has(j.status)); + jobs = jobs.slice(0, limit); + } + return jobs; } export async function updateIntakeJob( diff --git a/backend/src/modules/intake/routes.ts b/backend/src/modules/intake/routes.ts index 6f5e0e3..6f49181 100644 --- a/backend/src/modules/intake/routes.ts +++ b/backend/src/modules/intake/routes.ts @@ -23,7 +23,7 @@ import { UpdateIntakeRuleSchema, ListIntakeJobsQuerySchema, } from './types.js'; -import type { IntakeRuleDoc } from './types.js'; +import type { IntakeRuleDoc, IntakeJobStatus } from './types.js'; // ── Rate limiter (simple in-memory) ────────────────────────────── @@ -249,8 +249,12 @@ export async function intakeRoutes(app: FastifyInstance): Promise { const userId = getUserId(req); const productId = getRequestProductId(req); const query = ListIntakeJobsQuerySchema.parse(req.query); + // Support comma-separated statuses (e.g. "queued,extracting,processing") + const statuses = query.status + ? query.status.split(',').map((s) => s.trim()).filter(Boolean) as IntakeJobStatus[] + : undefined; const jobs = await repo.listIntakeJobs(userId, productId, { - status: query.status, + statuses, since: query.since, limit: query.limit, offset: query.offset, diff --git a/backend/src/modules/intake/types.ts b/backend/src/modules/intake/types.ts index 1570ec5..2393019 100644 --- a/backend/src/modules/intake/types.ts +++ b/backend/src/modules/intake/types.ts @@ -86,7 +86,7 @@ export const IntakeRequestSchema = z.object({ export type IntakeRequest = z.infer; export const ListIntakeJobsQuerySchema = z.object({ - status: z.enum(INTAKE_JOB_STATUSES).optional(), + status: z.string().max(200).optional(), since: z.string().max(64).optional(), limit: z.coerce.number().int().min(1).max(100).default(20), offset: z.coerce.number().int().min(0).default(0),