fix(logs): include subfolders when filtering logs by folder (#4525)

* fix(logs): include subfolders when filtering logs by folder

* fix(logs): use pop() for O(1) dequeue in folder BFS

* fix(logs): move folder expansion to server-only module to fix client bundle build
This commit is contained in:
Waleed
2026-05-08 16:56:06 -07:00
committed by GitHub
parent 630db5cee0
commit 50d4afd4a8
4 changed files with 71 additions and 0 deletions
+5
View File
@@ -6,6 +6,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { buildFilterConditions, LogFilterParamsSchema } from '@/lib/logs/filters'
import { expandFolderIdsWithDescendants } from '@/lib/logs/folder-expansion'
const logger = createLogger('LogsExportAPI')
@@ -45,6 +46,10 @@ export const GET = withRouteHandler(async (request: NextRequest) => {
workflowName: sql<string>`COALESCE(${workflow.name}, 'Deleted Workflow')`,
}
if (params.folderIds) {
params.folderIds = await expandFolderIdsWithDescendants(params.workspaceId, params.folderIds)
}
const workspaceCondition = eq(workflowExecutionLogs.workspaceId, params.workspaceId)
const filterConditions = buildFilterConditions(params)
const conditions = filterConditions
+5
View File
@@ -32,6 +32,7 @@ import { parseRequest } from '@/lib/api/server'
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { buildFilterConditions } from '@/lib/logs/filters'
import { expandFolderIdsWithDescendants } from '@/lib/logs/folder-expansion'
const logger = createLogger('LogsAPI')
@@ -162,6 +163,10 @@ export const GET = withRouteHandler(async (request: NextRequest) => {
}
}
if (params.folderIds) {
params.folderIds = await expandFolderIdsWithDescendants(params.workspaceId, params.folderIds)
}
const commonFilters = buildFilterConditions(params, { useSimpleLevelFilter: false })
if (commonFilters) workflowConditions.push(commonFilters)
+8
View File
@@ -14,6 +14,7 @@ import { getSession } from '@/lib/auth'
import { generateRequestId } from '@/lib/core/utils/request'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { buildFilterConditions } from '@/lib/logs/filters'
import { expandFolderIdsWithDescendants } from '@/lib/logs/folder-expansion'
const logger = createLogger('LogsStatsAPI')
@@ -37,6 +38,13 @@ export const GET = withRouteHandler(async (request: NextRequest) => {
const workspaceFilter = eq(workflowExecutionLogs.workspaceId, params.workspaceId)
if (params.folderIds) {
params.folderIds = await expandFolderIdsWithDescendants(
params.workspaceId,
params.folderIds
)
}
const commonFilters = buildFilterConditions(params, { useSimpleLevelFilter: true })
const whereCondition = commonFilters ? and(workspaceFilter, commonFilters) : workspaceFilter
+53
View File
@@ -0,0 +1,53 @@
import { db } from '@sim/db'
import { workflowFolder } from '@sim/db/schema'
import { and, eq, isNull } from 'drizzle-orm'
/**
* Expands a CSV of selected folder IDs to include every descendant folder in the
* workspace, so that filtering by a parent folder also matches workflows that
* live in nested subfolders.
*
* Returns the original CSV when there are no descendants (or when the input is
* empty / undefined). Unknown IDs are preserved so the caller's `inArray` check
* behaves the same as today (matches nothing).
*
* Server-only: pulls in the database client. Keep separate from `filters.ts`
* (imported by client hooks) to avoid leaking postgres into the browser bundle.
*/
export async function expandFolderIdsWithDescendants(
workspaceId: string,
folderIdsCsv: string | undefined
): Promise<string | undefined> {
if (!folderIdsCsv) return folderIdsCsv
const seedIds = folderIdsCsv.split(',').filter(Boolean)
if (seedIds.length === 0) return folderIdsCsv
const rows = await db
.select({ id: workflowFolder.id, parentId: workflowFolder.parentId })
.from(workflowFolder)
.where(and(eq(workflowFolder.workspaceId, workspaceId), isNull(workflowFolder.archivedAt)))
const childrenByParent = new Map<string, string[]>()
for (const row of rows) {
if (!row.parentId) continue
const list = childrenByParent.get(row.parentId)
if (list) list.push(row.id)
else childrenByParent.set(row.parentId, [row.id])
}
const expanded = new Set<string>(seedIds)
const queue = [...seedIds]
while (queue.length > 0) {
const current = queue.pop() as string
const children = childrenByParent.get(current)
if (!children) continue
for (const childId of children) {
if (!expanded.has(childId)) {
expanded.add(childId)
queue.push(childId)
}
}
}
return Array.from(expanded).join(',')
}