Skip to content

Commit d748a82

Browse files
committed
cleanup code
1 parent b464d70 commit d748a82

File tree

27 files changed

+713
-498
lines changed

27 files changed

+713
-498
lines changed

apps/sim/app/api/copilot/execute-tool/route.ts

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ import {
1414
import { generateRequestId } from '@/lib/core/utils/request'
1515
import { getEffectiveDecryptedEnv } from '@/lib/environment/utils'
1616
import { refreshTokenIfNeeded } from '@/app/api/auth/oauth/utils'
17-
import { REFERENCE } from '@/executor/constants'
18-
import { createEnvVarPattern } from '@/executor/utils/reference-validation'
17+
import { resolveEnvVarReferences } from '@/executor/utils/reference-validation'
1918
import { executeTool } from '@/tools'
2019
import { getTool, resolveToolId } from '@/tools/utils'
2120

@@ -28,45 +27,6 @@ const ExecuteToolSchema = z.object({
2827
workflowId: z.string().optional(),
2928
})
3029

31-
/**
32-
* Resolves all {{ENV_VAR}} references in a value recursively
33-
* Works with strings, arrays, and objects
34-
*/
35-
function resolveEnvVarReferences(value: any, envVars: Record<string, string>): any {
36-
if (typeof value === 'string') {
37-
// Check for exact match: entire string is "{{VAR_NAME}}"
38-
const exactMatchPattern = new RegExp(
39-
`^\\${REFERENCE.ENV_VAR_START}([^}]+)\\${REFERENCE.ENV_VAR_END}$`
40-
)
41-
const exactMatch = exactMatchPattern.exec(value)
42-
if (exactMatch) {
43-
const envVarName = exactMatch[1].trim()
44-
return envVars[envVarName] ?? value
45-
}
46-
47-
// Check for embedded references: "prefix {{VAR}} suffix"
48-
const envVarPattern = createEnvVarPattern()
49-
return value.replace(envVarPattern, (match, varName) => {
50-
const trimmedName = varName.trim()
51-
return envVars[trimmedName] ?? match
52-
})
53-
}
54-
55-
if (Array.isArray(value)) {
56-
return value.map((item) => resolveEnvVarReferences(item, envVars))
57-
}
58-
59-
if (value !== null && typeof value === 'object') {
60-
const resolved: Record<string, any> = {}
61-
for (const [key, val] of Object.entries(value)) {
62-
resolved[key] = resolveEnvVarReferences(val, envVars)
63-
}
64-
return resolved
65-
}
66-
67-
return value
68-
}
69-
7030
export async function POST(req: NextRequest) {
7131
const tracker = createRequestTracker()
7232

@@ -145,7 +105,17 @@ export async function POST(req: NextRequest) {
145105

146106
// Build execution params starting with LLM-provided arguments
147107
// Resolve all {{ENV_VAR}} references in the arguments
148-
const executionParams: Record<string, any> = resolveEnvVarReferences(toolArgs, decryptedEnvVars)
108+
const executionParams: Record<string, any> = resolveEnvVarReferences(
109+
toolArgs,
110+
decryptedEnvVars,
111+
{
112+
resolveExactMatch: true,
113+
allowEmbedded: true,
114+
trimKeys: true,
115+
onMissing: 'keep',
116+
deep: true,
117+
}
118+
) as Record<string, any>
149119

150120
logger.info(`[${tracker.requestId}] Resolved env var references in arguments`, {
151121
toolName,

apps/sim/app/api/function/execute/route.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { escapeRegExp, normalizeName, REFERENCE } from '@/executor/constants'
99
import {
1010
createEnvVarPattern,
1111
createWorkflowVariablePattern,
12+
resolveEnvVarReferences,
1213
} from '@/executor/utils/reference-validation'
1314
export const dynamic = 'force-dynamic'
1415
export const runtime = 'nodejs'
@@ -479,9 +480,29 @@ function resolveEnvironmentVariables(
479480
const replacements: Array<{ match: string; index: number; varName: string; varValue: string }> =
480481
[]
481482

483+
const resolverVars: Record<string, string> = {}
484+
Object.entries(params).forEach(([key, value]) => {
485+
if (value) {
486+
resolverVars[key] = String(value)
487+
}
488+
})
489+
Object.entries(envVars).forEach(([key, value]) => {
490+
if (value) {
491+
resolverVars[key] = value
492+
}
493+
})
494+
482495
while ((match = regex.exec(code)) !== null) {
483496
const varName = match[1].trim()
484-
const varValue = envVars[varName] || params[varName] || ''
497+
const resolved = resolveEnvVarReferences(match[0], resolverVars, {
498+
allowEmbedded: true,
499+
resolveExactMatch: true,
500+
trimKeys: true,
501+
onMissing: 'empty',
502+
deep: false,
503+
})
504+
const varValue =
505+
typeof resolved === 'string' ? resolved : resolved == null ? '' : String(resolved)
485506
replacements.push({
486507
match: match[0],
487508
index: match.index,

apps/sim/app/api/mcp/servers/test-connection/route.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import { McpClient } from '@/lib/mcp/client'
55
import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
66
import type { McpServerConfig, McpTransport } from '@/lib/mcp/types'
77
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
8-
import { REFERENCE } from '@/executor/constants'
9-
import { createEnvVarPattern } from '@/executor/utils/reference-validation'
8+
import { resolveEnvVarReferences } from '@/executor/utils/reference-validation'
109

1110
const logger = createLogger('McpServerTestAPI')
1211

@@ -24,22 +23,23 @@ function isUrlBasedTransport(transport: McpTransport): boolean {
2423
* Resolve environment variables in strings
2524
*/
2625
function resolveEnvVars(value: string, envVars: Record<string, string>): string {
27-
const envVarPattern = createEnvVarPattern()
28-
const envMatches = value.match(envVarPattern)
29-
if (!envMatches) return value
30-
31-
let resolvedValue = value
32-
for (const match of envMatches) {
33-
const envKey = match.slice(REFERENCE.ENV_VAR_START.length, -REFERENCE.ENV_VAR_END.length).trim()
34-
const envValue = envVars[envKey]
35-
36-
if (envValue === undefined) {
26+
const missingVars: string[] = []
27+
const resolvedValue = resolveEnvVarReferences(value, envVars, {
28+
allowEmbedded: true,
29+
resolveExactMatch: true,
30+
trimKeys: true,
31+
onMissing: 'keep',
32+
deep: false,
33+
missingKeys: missingVars,
34+
}) as string
35+
36+
if (missingVars.length > 0) {
37+
const uniqueMissing = Array.from(new Set(missingVars))
38+
uniqueMissing.forEach((envKey) => {
3739
logger.warn(`Environment variable "${envKey}" not found in MCP server test`)
38-
continue
39-
}
40-
41-
resolvedValue = resolvedValue.replace(match, envValue)
40+
})
4241
}
42+
4343
return resolvedValue
4444
}
4545

apps/sim/app/api/schedules/execute/route.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
import type { NextRequest } from 'next/server'
77
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
88

9+
// Mock the preflight module before any imports to avoid cascade of db/schema imports
10+
vi.mock('@/lib/workflows/executor/preflight', () => ({
11+
preflightWorkflowEnvVars: vi.fn().mockResolvedValue(undefined),
12+
}))
13+
914
function createMockRequest(): NextRequest {
1015
const mockHeaders = new Map([
1116
['authorization', 'Bearer test-cron-secret'],
@@ -93,6 +98,11 @@ describe('Scheduled Workflow Execution API Route', () => {
9398
nextRunAt: 'nextRunAt',
9499
lastQueuedAt: 'lastQueuedAt',
95100
},
101+
workflow: {
102+
id: 'id',
103+
userId: 'userId',
104+
workspaceId: 'workspaceId',
105+
},
96106
}
97107
})
98108

@@ -170,6 +180,11 @@ describe('Scheduled Workflow Execution API Route', () => {
170180
nextRunAt: 'nextRunAt',
171181
lastQueuedAt: 'lastQueuedAt',
172182
},
183+
workflow: {
184+
id: 'id',
185+
userId: 'userId',
186+
workspaceId: 'workspaceId',
187+
},
173188
}
174189
})
175190

@@ -229,6 +244,11 @@ describe('Scheduled Workflow Execution API Route', () => {
229244
nextRunAt: 'nextRunAt',
230245
lastQueuedAt: 'lastQueuedAt',
231246
},
247+
workflow: {
248+
id: 'id',
249+
userId: 'userId',
250+
workspaceId: 'workspaceId',
251+
},
232252
}
233253
})
234254

@@ -311,6 +331,11 @@ describe('Scheduled Workflow Execution API Route', () => {
311331
nextRunAt: 'nextRunAt',
312332
lastQueuedAt: 'lastQueuedAt',
313333
},
334+
workflow: {
335+
id: 'id',
336+
userId: 'userId',
337+
workspaceId: 'workspaceId',
338+
},
314339
}
315340
})
316341

apps/sim/app/api/schedules/execute/route.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { db, workflowSchedule } from '@sim/db'
1+
import { db, workflow, workflowSchedule } from '@sim/db'
22
import { createLogger } from '@sim/logger'
33
import { tasks } from '@trigger.dev/sdk'
44
import { and, eq, isNull, lt, lte, not, or } from 'drizzle-orm'
55
import { type NextRequest, NextResponse } from 'next/server'
66
import { verifyCronAuth } from '@/lib/auth/internal'
77
import { isTriggerDevEnabled } from '@/lib/core/config/feature-flags'
88
import { generateRequestId } from '@/lib/core/utils/request'
9+
import { preflightWorkflowEnvVars } from '@/lib/workflows/executor/preflight'
910
import { executeScheduleJob } from '@/background/schedule-execution'
1011

1112
export const dynamic = 'force-dynamic'
@@ -68,6 +69,39 @@ export async function GET(request: NextRequest) {
6869
failedCount: schedule.failedCount || 0,
6970
now: queueTime.toISOString(),
7071
scheduledFor: schedule.nextRunAt?.toISOString(),
72+
preflighted: true,
73+
}
74+
75+
const [workflowRecord] = await db
76+
.select({ userId: workflow.userId, workspaceId: workflow.workspaceId })
77+
.from(workflow)
78+
.where(eq(workflow.id, schedule.workflowId))
79+
.limit(1)
80+
81+
if (!workflowRecord?.userId || !workflowRecord.workspaceId) {
82+
logger.warn(
83+
`[${requestId}] Missing workflow metadata for preflight. Skipping Trigger.dev enqueue.`,
84+
{ workflowId: schedule.workflowId }
85+
)
86+
await executeScheduleJob({ ...payload, preflighted: false })
87+
return null
88+
}
89+
90+
try {
91+
await preflightWorkflowEnvVars({
92+
workflowId: schedule.workflowId,
93+
workspaceId: workflowRecord.workspaceId,
94+
envUserId: workflowRecord.userId,
95+
requestId,
96+
useDraftState: false,
97+
})
98+
} catch (error) {
99+
logger.warn(
100+
`[${requestId}] Env preflight failed. Skipping Trigger.dev enqueue for workflow ${schedule.workflowId}`,
101+
{ error: error instanceof Error ? error.message : String(error) }
102+
)
103+
await executeScheduleJob({ ...payload, preflighted: false })
104+
return null
71105
}
72106

73107
const handle = await tasks.trigger('schedule-execution', payload)

apps/sim/app/api/workflows/[id]/execute/route.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ type AsyncExecutionParams = {
110110
userId: string
111111
input: any
112112
triggerType: CoreTriggerType
113+
preflighted?: boolean
113114
}
114115

115116
/**
@@ -132,6 +133,7 @@ async function handleAsyncExecution(params: AsyncExecutionParams): Promise<NextR
132133
userId,
133134
input,
134135
triggerType,
136+
preflighted: params.preflighted,
135137
}
136138

137139
try {
@@ -264,6 +266,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
264266
requestId
265267
)
266268

269+
const shouldPreflightEnvVars = isAsyncMode && isTriggerDevEnabled
267270
const preprocessResult = await preprocessExecution({
268271
workflowId,
269272
userId,
@@ -272,6 +275,9 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
272275
requestId,
273276
checkDeployment: !shouldUseDraftState,
274277
loggingSession,
278+
preflightEnvVars: shouldPreflightEnvVars,
279+
useDraftState: shouldUseDraftState,
280+
envUserId: isClientSession ? userId : undefined,
275281
})
276282

277283
if (!preprocessResult.success) {
@@ -303,6 +309,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
303309
userId: actorUserId,
304310
input,
305311
triggerType: loggingTriggerType,
312+
preflighted: shouldPreflightEnvVars,
306313
})
307314
}
308315

0 commit comments

Comments
 (0)