Skip to content

Commit f1efc59

Browse files
waleedlatif1claude
andauthored
fix(selectors): resolve env var references at design time for selector context (#3446)
* fix(selectors): resolve env var references at design time for selector context Selectors now resolve {{ENV_VAR}} references before building context and returning dependency values to consumers, enabling env-var-based credentials (e.g. {{SLACK_BOT_TOKEN}}) to work with selector dropdowns. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(selectors): prevent unresolved env var templates from leaking into context - Fall back to undefined instead of raw template string when env var is missing from store, so the null-check in the context loop discards it - Use resolvedDetailId in query cache key so React Query refetches when the underlying env var value changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(selectors): use || for consistent empty-string env var handling Align use-selector-setup.ts with use-selector-query.ts by using || instead of ?? so empty-string env var values are treated as unset. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 244cf4f commit f1efc59

File tree

2 files changed

+43
-10
lines changed

2 files changed

+43
-10
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-selector-setup.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
import { useMemo } from 'react'
44
import { useParams } from 'next/navigation'
55
import type { SubBlockConfig } from '@/blocks/types'
6-
import { isEnvVarReference, isReference } from '@/executor/constants'
6+
import { extractEnvVarName, isEnvVarReference, isReference } from '@/executor/constants'
77
import type { SelectorContext, SelectorKey } from '@/hooks/selectors/types'
8+
import { useEnvironmentStore } from '@/stores/settings/environment'
89
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
910
import { useDependsOnGate } from './use-depends-on-gate'
1011

@@ -30,23 +31,43 @@ export function useSelectorSetup(
3031
const activeWorkflowId = useWorkflowRegistry((s) => s.activeWorkflowId)
3132
const workflowId = (params?.workflowId as string) || activeWorkflowId || ''
3233

34+
const envVariables = useEnvironmentStore((s) => s.variables)
35+
3336
const { finalDisabled, dependencyValues, canonicalIndex } = useDependsOnGate(
3437
blockId,
3538
subBlock,
3639
opts
3740
)
3841

42+
const resolvedDependencyValues = useMemo(() => {
43+
const resolved: Record<string, unknown> = {}
44+
for (const [key, value] of Object.entries(dependencyValues)) {
45+
if (value === null || value === undefined) {
46+
resolved[key] = value
47+
continue
48+
}
49+
const str = String(value)
50+
if (isEnvVarReference(str)) {
51+
const varName = extractEnvVarName(str)
52+
resolved[key] = envVariables[varName]?.value || undefined
53+
} else {
54+
resolved[key] = value
55+
}
56+
}
57+
return resolved
58+
}, [dependencyValues, envVariables])
59+
3960
const selectorContext = useMemo<SelectorContext>(() => {
4061
const context: SelectorContext = {
4162
workflowId,
4263
mimeType: subBlock.mimeType,
4364
}
4465

45-
for (const [depKey, value] of Object.entries(dependencyValues)) {
66+
for (const [depKey, value] of Object.entries(resolvedDependencyValues)) {
4667
if (value === null || value === undefined) continue
4768
const strValue = String(value)
4869
if (!strValue) continue
49-
if (isReference(strValue) || isEnvVarReference(strValue)) continue
70+
if (isReference(strValue)) continue
5071

5172
const canonicalParamId = canonicalIndex.canonicalIdBySubBlockId[depKey] ?? depKey
5273

@@ -58,14 +79,14 @@ export function useSelectorSetup(
5879
}
5980

6081
return context
61-
}, [dependencyValues, canonicalIndex, workflowId, subBlock.mimeType])
82+
}, [resolvedDependencyValues, canonicalIndex, workflowId, subBlock.mimeType])
6283

6384
return {
6485
selectorKey: (subBlock.selectorKey ?? null) as SelectorKey | null,
6586
selectorContext,
6687
allowSearch: subBlock.selectorAllowSearch ?? true,
6788
disabled: finalDisabled || !subBlock.selectorKey,
68-
dependencyValues,
89+
dependencyValues: resolvedDependencyValues,
6990
}
7091
}
7192

apps/sim/hooks/selectors/use-selector-query.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { useMemo } from 'react'
22
import { useQuery } from '@tanstack/react-query'
3-
import { isEnvVarReference, isReference } from '@/executor/constants'
3+
import { extractEnvVarName, isEnvVarReference, isReference } from '@/executor/constants'
44
import { getSelectorDefinition, mergeOption } from '@/hooks/selectors/registry'
55
import type { SelectorKey, SelectorOption, SelectorQueryArgs } from '@/hooks/selectors/types'
6+
import { useEnvironmentStore } from '@/stores/settings/environment'
67

78
interface SelectorHookArgs extends Omit<SelectorQueryArgs, 'key'> {
89
search?: string
@@ -30,14 +31,25 @@ export function useSelectorOptionDetail(
3031
key: SelectorKey,
3132
args: SelectorHookArgs & { detailId?: string }
3233
) {
34+
const envVariables = useEnvironmentStore((s) => s.variables)
3335
const definition = getSelectorDefinition(key)
36+
37+
const resolvedDetailId = useMemo(() => {
38+
if (!args.detailId) return undefined
39+
if (isReference(args.detailId)) return undefined
40+
if (isEnvVarReference(args.detailId)) {
41+
const varName = extractEnvVarName(args.detailId)
42+
return envVariables[varName]?.value || undefined
43+
}
44+
return args.detailId
45+
}, [args.detailId, envVariables])
46+
3447
const queryArgs: SelectorQueryArgs = {
3548
key,
3649
context: args.context,
37-
detailId: args.detailId,
50+
detailId: resolvedDetailId,
3851
}
39-
const hasRealDetailId =
40-
Boolean(args.detailId) && !isReference(args.detailId!) && !isEnvVarReference(args.detailId!)
52+
const hasRealDetailId = Boolean(resolvedDetailId)
4153
const baseEnabled =
4254
hasRealDetailId && definition.fetchById !== undefined
4355
? definition.enabled
@@ -47,7 +59,7 @@ export function useSelectorOptionDetail(
4759
const enabled = args.enabled ?? baseEnabled
4860

4961
const query = useQuery<SelectorOption | null>({
50-
queryKey: [...definition.getQueryKey(queryArgs), 'detail', args.detailId ?? 'none'],
62+
queryKey: [...definition.getQueryKey(queryArgs), 'detail', resolvedDetailId ?? 'none'],
5163
queryFn: () => definition.fetchById!(queryArgs),
5264
enabled,
5365
staleTime: definition.staleTime ?? 300_000,

0 commit comments

Comments
 (0)