Skip to content

Commit 50d4afd

Browse files
authored
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
1 parent 630db5c commit 50d4afd

4 files changed

Lines changed: 71 additions & 0 deletions

File tree

apps/sim/app/api/logs/export/route.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { type NextRequest, NextResponse } from 'next/server'
66
import { getSession } from '@/lib/auth'
77
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
88
import { buildFilterConditions, LogFilterParamsSchema } from '@/lib/logs/filters'
9+
import { expandFolderIdsWithDescendants } from '@/lib/logs/folder-expansion'
910

1011
const logger = createLogger('LogsExportAPI')
1112

@@ -45,6 +46,10 @@ export const GET = withRouteHandler(async (request: NextRequest) => {
4546
workflowName: sql<string>`COALESCE(${workflow.name}, 'Deleted Workflow')`,
4647
}
4748

49+
if (params.folderIds) {
50+
params.folderIds = await expandFolderIdsWithDescendants(params.workspaceId, params.folderIds)
51+
}
52+
4853
const workspaceCondition = eq(workflowExecutionLogs.workspaceId, params.workspaceId)
4954
const filterConditions = buildFilterConditions(params)
5055
const conditions = filterConditions

apps/sim/app/api/logs/route.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { parseRequest } from '@/lib/api/server'
3232
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
3333
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
3434
import { buildFilterConditions } from '@/lib/logs/filters'
35+
import { expandFolderIdsWithDescendants } from '@/lib/logs/folder-expansion'
3536

3637
const logger = createLogger('LogsAPI')
3738

@@ -162,6 +163,10 @@ export const GET = withRouteHandler(async (request: NextRequest) => {
162163
}
163164
}
164165

166+
if (params.folderIds) {
167+
params.folderIds = await expandFolderIdsWithDescendants(params.workspaceId, params.folderIds)
168+
}
169+
165170
const commonFilters = buildFilterConditions(params, { useSimpleLevelFilter: false })
166171
if (commonFilters) workflowConditions.push(commonFilters)
167172

apps/sim/app/api/logs/stats/route.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { getSession } from '@/lib/auth'
1414
import { generateRequestId } from '@/lib/core/utils/request'
1515
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
1616
import { buildFilterConditions } from '@/lib/logs/filters'
17+
import { expandFolderIdsWithDescendants } from '@/lib/logs/folder-expansion'
1718

1819
const logger = createLogger('LogsStatsAPI')
1920

@@ -37,6 +38,13 @@ export const GET = withRouteHandler(async (request: NextRequest) => {
3738

3839
const workspaceFilter = eq(workflowExecutionLogs.workspaceId, params.workspaceId)
3940

41+
if (params.folderIds) {
42+
params.folderIds = await expandFolderIdsWithDescendants(
43+
params.workspaceId,
44+
params.folderIds
45+
)
46+
}
47+
4048
const commonFilters = buildFilterConditions(params, { useSimpleLevelFilter: true })
4149
const whereCondition = commonFilters ? and(workspaceFilter, commonFilters) : workspaceFilter
4250

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { db } from '@sim/db'
2+
import { workflowFolder } from '@sim/db/schema'
3+
import { and, eq, isNull } from 'drizzle-orm'
4+
5+
/**
6+
* Expands a CSV of selected folder IDs to include every descendant folder in the
7+
* workspace, so that filtering by a parent folder also matches workflows that
8+
* live in nested subfolders.
9+
*
10+
* Returns the original CSV when there are no descendants (or when the input is
11+
* empty / undefined). Unknown IDs are preserved so the caller's `inArray` check
12+
* behaves the same as today (matches nothing).
13+
*
14+
* Server-only: pulls in the database client. Keep separate from `filters.ts`
15+
* (imported by client hooks) to avoid leaking postgres into the browser bundle.
16+
*/
17+
export async function expandFolderIdsWithDescendants(
18+
workspaceId: string,
19+
folderIdsCsv: string | undefined
20+
): Promise<string | undefined> {
21+
if (!folderIdsCsv) return folderIdsCsv
22+
const seedIds = folderIdsCsv.split(',').filter(Boolean)
23+
if (seedIds.length === 0) return folderIdsCsv
24+
25+
const rows = await db
26+
.select({ id: workflowFolder.id, parentId: workflowFolder.parentId })
27+
.from(workflowFolder)
28+
.where(and(eq(workflowFolder.workspaceId, workspaceId), isNull(workflowFolder.archivedAt)))
29+
30+
const childrenByParent = new Map<string, string[]>()
31+
for (const row of rows) {
32+
if (!row.parentId) continue
33+
const list = childrenByParent.get(row.parentId)
34+
if (list) list.push(row.id)
35+
else childrenByParent.set(row.parentId, [row.id])
36+
}
37+
38+
const expanded = new Set<string>(seedIds)
39+
const queue = [...seedIds]
40+
while (queue.length > 0) {
41+
const current = queue.pop() as string
42+
const children = childrenByParent.get(current)
43+
if (!children) continue
44+
for (const childId of children) {
45+
if (!expanded.has(childId)) {
46+
expanded.add(childId)
47+
queue.push(childId)
48+
}
49+
}
50+
}
51+
52+
return Array.from(expanded).join(',')
53+
}

0 commit comments

Comments
 (0)