mirror of
https://github.com/simstudioai/sim.git
synced 2026-03-15 03:00:33 -04:00
Compare commits
4 Commits
improvemen
...
improvemen
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc5df60d8f | ||
|
|
adea9db89d | ||
|
|
94abc424be | ||
|
|
c1c6ed66d1 |
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useMemo } from 'react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { SELECTOR_CONTEXT_FIELDS } from '@/lib/workflows/subblocks/context'
|
||||
import type { SubBlockConfig } from '@/blocks/types'
|
||||
import { extractEnvVarName, isEnvVarReference, isReference } from '@/executor/constants'
|
||||
import type { SelectorContext, SelectorKey } from '@/hooks/selectors/types'
|
||||
@@ -14,8 +15,7 @@ import { useDependsOnGate } from './use-depends-on-gate'
|
||||
*
|
||||
* Builds a `SelectorContext` by mapping each `dependsOn` entry through the
|
||||
* canonical index to its `canonicalParamId`, which maps directly to
|
||||
* `SelectorContext` field names (e.g. `siteId`, `teamId`, `collectionId`).
|
||||
* The one special case is `oauthCredential` which maps to `credentialId`.
|
||||
* `SelectorContext` field names (e.g. `siteId`, `teamId`, `oauthCredential`).
|
||||
*
|
||||
* @param blockId - The block containing the selector sub-block
|
||||
* @param subBlock - The sub-block config (must have `selectorKey` set)
|
||||
@@ -70,11 +70,8 @@ export function useSelectorSetup(
|
||||
if (isReference(strValue)) continue
|
||||
|
||||
const canonicalParamId = canonicalIndex.canonicalIdBySubBlockId[depKey] ?? depKey
|
||||
|
||||
if (canonicalParamId === 'oauthCredential') {
|
||||
context.credentialId = strValue
|
||||
} else if (canonicalParamId in CONTEXT_FIELD_SET) {
|
||||
;(context as Record<string, unknown>)[canonicalParamId] = strValue
|
||||
if (SELECTOR_CONTEXT_FIELDS.has(canonicalParamId as keyof SelectorContext)) {
|
||||
context[canonicalParamId as keyof SelectorContext] = strValue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,19 +86,3 @@ export function useSelectorSetup(
|
||||
dependencyValues: resolvedDependencyValues,
|
||||
}
|
||||
}
|
||||
|
||||
const CONTEXT_FIELD_SET: Record<string, true> = {
|
||||
credentialId: true,
|
||||
domain: true,
|
||||
teamId: true,
|
||||
projectId: true,
|
||||
knowledgeBaseId: true,
|
||||
planId: true,
|
||||
siteId: true,
|
||||
collectionId: true,
|
||||
spreadsheetId: true,
|
||||
fileId: true,
|
||||
baseId: true,
|
||||
datasetId: true,
|
||||
serviceDeskId: true,
|
||||
}
|
||||
|
||||
@@ -57,9 +57,9 @@ import { useWebhookManagement } from '@/hooks/use-webhook-management'
|
||||
const SLACK_OVERRIDES: SelectorOverrides = {
|
||||
transformContext: (context, deps) => {
|
||||
const authMethod = deps.authMethod as string
|
||||
const credentialId =
|
||||
const oauthCredential =
|
||||
authMethod === 'bot_token' ? String(deps.botToken ?? '') : String(deps.credential ?? '')
|
||||
return { ...context, credentialId }
|
||||
return { ...context, oauthCredential }
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -578,7 +578,7 @@ const SubBlockRow = memo(function SubBlockRow({
|
||||
subBlock,
|
||||
value: rawValue,
|
||||
workflowId,
|
||||
credentialId: typeof credentialId === 'string' ? credentialId : undefined,
|
||||
oauthCredential: typeof credentialId === 'string' ? credentialId : undefined,
|
||||
knowledgeBaseId: typeof knowledgeBaseId === 'string' ? knowledgeBaseId : undefined,
|
||||
domain: domainValue,
|
||||
teamId: teamIdValue,
|
||||
|
||||
@@ -39,10 +39,10 @@ type FolderResponse = { id: string; name: string }
|
||||
type PlannerTask = { id: string; title: string }
|
||||
|
||||
const ensureCredential = (context: SelectorContext, key: SelectorKey): string => {
|
||||
if (!context.credentialId) {
|
||||
if (!context.oauthCredential) {
|
||||
throw new Error(`Missing credential for selector ${key}`)
|
||||
}
|
||||
return context.credentialId
|
||||
return context.oauthCredential
|
||||
}
|
||||
|
||||
const ensureDomain = (context: SelectorContext, key: SelectorKey): string => {
|
||||
@@ -66,9 +66,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'airtable.bases',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'airtable.bases')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -104,10 +104,10 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'airtable.tables',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.baseId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.baseId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.baseId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'airtable.tables')
|
||||
if (!context.baseId) {
|
||||
@@ -151,9 +151,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'asana.workspaces',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'asana.workspaces')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -182,9 +182,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'attio.objects',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'attio.objects')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -216,9 +216,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'attio.lists',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'attio.lists')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -250,10 +250,10 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'bigquery.datasets',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.projectId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.projectId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.projectId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'bigquery.datasets')
|
||||
if (!context.projectId) throw new Error('Missing project ID for bigquery.datasets selector')
|
||||
@@ -298,12 +298,12 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'bigquery.tables',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.projectId ?? 'none',
|
||||
context.datasetId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) =>
|
||||
Boolean(context.credentialId && context.projectId && context.datasetId),
|
||||
Boolean(context.oauthCredential && context.projectId && context.datasetId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'bigquery.tables')
|
||||
if (!context.projectId) throw new Error('Missing project ID for bigquery.tables selector')
|
||||
@@ -347,9 +347,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'calcom.eventTypes',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'calcom.eventTypes')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -381,9 +381,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'calcom.schedules',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'calcom.schedules')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -415,10 +415,10 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'confluence.spaces',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.domain ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.domain),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.domain),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'confluence.spaces')
|
||||
const domain = ensureDomain(context, 'confluence.spaces')
|
||||
@@ -460,10 +460,10 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'jsm.serviceDesks',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.domain ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.domain),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.domain),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'jsm.serviceDesks')
|
||||
const domain = ensureDomain(context, 'jsm.serviceDesks')
|
||||
@@ -505,12 +505,12 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'jsm.requestTypes',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.domain ?? 'none',
|
||||
context.serviceDeskId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) =>
|
||||
Boolean(context.credentialId && context.domain && context.serviceDeskId),
|
||||
Boolean(context.oauthCredential && context.domain && context.serviceDeskId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'jsm.requestTypes')
|
||||
const domain = ensureDomain(context, 'jsm.requestTypes')
|
||||
@@ -556,9 +556,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'google.tasks.lists',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'google.tasks.lists')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -587,9 +587,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'microsoft.planner.plans',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'microsoft.planner.plans')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -618,9 +618,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'notion.databases',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'notion.databases')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -652,9 +652,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'notion.pages',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'notion.pages')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -686,9 +686,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'pipedrive.pipelines',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'pipedrive.pipelines')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -720,10 +720,10 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'sharepoint.lists',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.siteId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.siteId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.siteId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'sharepoint.lists')
|
||||
if (!context.siteId) throw new Error('Missing site ID for sharepoint.lists selector')
|
||||
@@ -761,9 +761,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'trello.boards',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'trello.boards')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -794,9 +794,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'zoom.meetings',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'zoom.meetings')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -828,12 +828,12 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'slack.channels',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const body = JSON.stringify({
|
||||
credential: context.credentialId,
|
||||
credential: context.oauthCredential,
|
||||
workflowId: context.workflowId,
|
||||
})
|
||||
const data = await fetchJson<{ channels: SlackChannel[] }>('/api/tools/slack/channels', {
|
||||
@@ -852,12 +852,12 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'slack.users',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const body = JSON.stringify({
|
||||
credential: context.credentialId,
|
||||
credential: context.oauthCredential,
|
||||
workflowId: context.workflowId,
|
||||
})
|
||||
const data = await fetchJson<{ users: SlackUser[] }>('/api/tools/slack/users', {
|
||||
@@ -876,12 +876,12 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'gmail.labels',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const data = await fetchJson<{ labels: FolderResponse[] }>('/api/tools/gmail/labels', {
|
||||
searchParams: { credentialId: context.credentialId },
|
||||
searchParams: { credentialId: context.oauthCredential },
|
||||
})
|
||||
return (data.labels || []).map((label) => ({
|
||||
id: label.id,
|
||||
@@ -895,12 +895,12 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'outlook.folders',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const data = await fetchJson<{ folders: FolderResponse[] }>('/api/tools/outlook/folders', {
|
||||
searchParams: { credentialId: context.credentialId },
|
||||
searchParams: { credentialId: context.oauthCredential },
|
||||
})
|
||||
return (data.folders || []).map((folder) => ({
|
||||
id: folder.id,
|
||||
@@ -914,13 +914,13 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'google.calendar',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const data = await fetchJson<{ calendars: { id: string; summary: string }[] }>(
|
||||
'/api/tools/google_calendar/calendars',
|
||||
{ searchParams: { credentialId: context.credentialId } }
|
||||
{ searchParams: { credentialId: context.oauthCredential } }
|
||||
)
|
||||
return (data.calendars || []).map((calendar) => ({
|
||||
id: calendar.id,
|
||||
@@ -934,11 +934,11 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'microsoft.teams',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const body = JSON.stringify({ credential: context.credentialId })
|
||||
const body = JSON.stringify({ credential: context.oauthCredential })
|
||||
const data = await fetchJson<{ teams: { id: string; displayName: string }[] }>(
|
||||
'/api/tools/microsoft-teams/teams',
|
||||
{ method: 'POST', body }
|
||||
@@ -955,11 +955,11 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'microsoft.chats',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const body = JSON.stringify({ credential: context.credentialId })
|
||||
const body = JSON.stringify({ credential: context.oauthCredential })
|
||||
const data = await fetchJson<{ chats: { id: string; displayName: string }[] }>(
|
||||
'/api/tools/microsoft-teams/chats',
|
||||
{ method: 'POST', body }
|
||||
@@ -976,13 +976,13 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'microsoft.channels',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.teamId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.teamId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.teamId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const body = JSON.stringify({
|
||||
credential: context.credentialId,
|
||||
credential: context.oauthCredential,
|
||||
teamId: context.teamId,
|
||||
})
|
||||
const data = await fetchJson<{ channels: { id: string; displayName: string }[] }>(
|
||||
@@ -1001,14 +1001,14 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'wealthbox.contacts',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const data = await fetchJson<{ items: { id: string; name: string }[] }>(
|
||||
'/api/tools/wealthbox/items',
|
||||
{
|
||||
searchParams: { credentialId: context.credentialId, type: 'contact' },
|
||||
searchParams: { credentialId: context.oauthCredential, type: 'contact' },
|
||||
}
|
||||
)
|
||||
return (data.items || []).map((item) => ({
|
||||
@@ -1023,9 +1023,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'sharepoint.sites',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'sharepoint.sites')
|
||||
const body = JSON.stringify({
|
||||
@@ -1069,10 +1069,10 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'microsoft.planner',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.planId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.planId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.planId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'microsoft.planner')
|
||||
const body = JSON.stringify({
|
||||
@@ -1112,11 +1112,11 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context, search }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'jira.projects',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.domain ?? 'none',
|
||||
search ?? '',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.domain),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.domain),
|
||||
fetchList: async ({ context, search }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'jira.projects')
|
||||
const domain = ensureDomain(context, 'jira.projects')
|
||||
@@ -1171,12 +1171,12 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context, search }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'jira.issues',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.domain ?? 'none',
|
||||
context.projectId ?? 'none',
|
||||
search ?? '',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.domain),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.domain),
|
||||
fetchList: async ({ context, search }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'jira.issues')
|
||||
const domain = ensureDomain(context, 'jira.issues')
|
||||
@@ -1235,9 +1235,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'linear.teams',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'linear.teams')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -1260,10 +1260,10 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'linear.projects',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.teamId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.teamId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.teamId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'linear.projects')
|
||||
const body = JSON.stringify({
|
||||
@@ -1290,11 +1290,11 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context, search }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'confluence.pages',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.domain ?? 'none',
|
||||
search ?? '',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.domain),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.domain),
|
||||
fetchList: async ({ context, search }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'confluence.pages')
|
||||
const domain = ensureDomain(context, 'confluence.pages')
|
||||
@@ -1343,9 +1343,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'onedrive.files',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'onedrive.files')
|
||||
const data = await fetchJson<{ files: { id: string; name: string }[] }>(
|
||||
@@ -1366,9 +1366,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'onedrive.folders',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'onedrive.folders')
|
||||
const data = await fetchJson<{ files: { id: string; name: string }[] }>(
|
||||
@@ -1389,12 +1389,12 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context, search }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'google.drive',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.mimeType ?? 'any',
|
||||
context.fileId ?? 'root',
|
||||
search ?? '',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context, search }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'google.drive')
|
||||
const data = await fetchJson<{ files: { id: string; name: string }[] }>(
|
||||
@@ -1438,10 +1438,10 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'google.sheets',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.spreadsheetId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.spreadsheetId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.spreadsheetId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'google.sheets')
|
||||
if (!context.spreadsheetId) {
|
||||
@@ -1469,10 +1469,10 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'microsoft.excel.sheets',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.spreadsheetId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.spreadsheetId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.spreadsheetId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'microsoft.excel.sheets')
|
||||
if (!context.spreadsheetId) {
|
||||
@@ -1500,10 +1500,10 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context, search }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'microsoft.excel',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
search ?? '',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context, search }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'microsoft.excel')
|
||||
const data = await fetchJson<{ files: { id: string; name: string }[] }>(
|
||||
@@ -1528,10 +1528,10 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context, search }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'microsoft.word',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
search ?? '',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context, search }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'microsoft.word')
|
||||
const data = await fetchJson<{ files: { id: string; name: string }[] }>(
|
||||
@@ -1596,9 +1596,9 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'webflow.sites',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'webflow.sites')
|
||||
const body = JSON.stringify({ credential: credentialId, workflowId: context.workflowId })
|
||||
@@ -1621,10 +1621,10 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'webflow.collections',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.siteId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.siteId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.siteId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'webflow.collections')
|
||||
if (!context.siteId) {
|
||||
@@ -1654,11 +1654,11 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
getQueryKey: ({ context, search }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'webflow.items',
|
||||
context.credentialId ?? 'none',
|
||||
context.oauthCredential ?? 'none',
|
||||
context.collectionId ?? 'none',
|
||||
search ?? '',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.collectionId),
|
||||
enabled: ({ context }) => Boolean(context.oauthCredential && context.collectionId),
|
||||
fetchList: async ({ context, search }: SelectorQueryArgs) => {
|
||||
const credentialId = ensureCredential(context, 'webflow.items')
|
||||
if (!context.collectionId) {
|
||||
|
||||
@@ -7,46 +7,16 @@ export interface SelectorResolution {
|
||||
allowSearch: boolean
|
||||
}
|
||||
|
||||
export interface SelectorResolutionArgs {
|
||||
workflowId?: string
|
||||
credentialId?: string
|
||||
domain?: string
|
||||
projectId?: string
|
||||
planId?: string
|
||||
teamId?: string
|
||||
knowledgeBaseId?: string
|
||||
siteId?: string
|
||||
collectionId?: string
|
||||
spreadsheetId?: string
|
||||
fileId?: string
|
||||
baseId?: string
|
||||
datasetId?: string
|
||||
serviceDeskId?: string
|
||||
}
|
||||
|
||||
export function resolveSelectorForSubBlock(
|
||||
subBlock: SubBlockConfig,
|
||||
args: SelectorResolutionArgs
|
||||
context: SelectorContext
|
||||
): SelectorResolution | null {
|
||||
if (!subBlock.selectorKey) return null
|
||||
return {
|
||||
key: subBlock.selectorKey,
|
||||
context: {
|
||||
workflowId: args.workflowId,
|
||||
credentialId: args.credentialId,
|
||||
domain: args.domain,
|
||||
projectId: args.projectId,
|
||||
planId: args.planId,
|
||||
teamId: args.teamId,
|
||||
knowledgeBaseId: args.knowledgeBaseId,
|
||||
siteId: args.siteId,
|
||||
collectionId: args.collectionId,
|
||||
spreadsheetId: args.spreadsheetId,
|
||||
fileId: args.fileId,
|
||||
baseId: args.baseId,
|
||||
datasetId: args.datasetId,
|
||||
serviceDeskId: args.serviceDeskId,
|
||||
mimeType: subBlock.mimeType,
|
||||
...context,
|
||||
mimeType: subBlock.mimeType ?? context.mimeType,
|
||||
},
|
||||
allowSearch: subBlock.selectorAllowSearch ?? true,
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export interface SelectorOption {
|
||||
export interface SelectorContext {
|
||||
workspaceId?: string
|
||||
workflowId?: string
|
||||
credentialId?: string
|
||||
oauthCredential?: string
|
||||
serviceId?: string
|
||||
domain?: string
|
||||
teamId?: string
|
||||
|
||||
@@ -12,7 +12,7 @@ interface SelectorDisplayNameArgs {
|
||||
subBlock?: SubBlockConfig
|
||||
value: unknown
|
||||
workflowId?: string
|
||||
credentialId?: string
|
||||
oauthCredential?: string
|
||||
domain?: string
|
||||
projectId?: string
|
||||
planId?: string
|
||||
@@ -31,7 +31,7 @@ export function useSelectorDisplayName({
|
||||
subBlock,
|
||||
value,
|
||||
workflowId,
|
||||
credentialId,
|
||||
oauthCredential,
|
||||
domain,
|
||||
projectId,
|
||||
planId,
|
||||
@@ -51,7 +51,7 @@ export function useSelectorDisplayName({
|
||||
if (!subBlock || !detailId) return null
|
||||
return resolveSelectorForSubBlock(subBlock, {
|
||||
workflowId,
|
||||
credentialId,
|
||||
oauthCredential,
|
||||
domain,
|
||||
projectId,
|
||||
planId,
|
||||
@@ -69,7 +69,7 @@ export function useSelectorDisplayName({
|
||||
subBlock,
|
||||
detailId,
|
||||
workflowId,
|
||||
credentialId,
|
||||
oauthCredential,
|
||||
domain,
|
||||
projectId,
|
||||
planId,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { buildSelectorContextFromBlock } from '@/lib/workflows/subblocks/context'
|
||||
import { getBlock } from '@/blocks/registry'
|
||||
import { SELECTOR_TYPES_HYDRATION_REQUIRED, type SubBlockConfig } from '@/blocks/types'
|
||||
import { CREDENTIAL_SET, isUuid } from '@/executor/constants'
|
||||
@@ -6,7 +7,7 @@ import { fetchCredentialSetById } from '@/hooks/queries/credential-sets'
|
||||
import { fetchOAuthCredentialDetail } from '@/hooks/queries/oauth-credentials'
|
||||
import { getSelectorDefinition } from '@/hooks/selectors/registry'
|
||||
import { resolveSelectorForSubBlock } from '@/hooks/selectors/resolution'
|
||||
import type { SelectorKey } from '@/hooks/selectors/types'
|
||||
import type { SelectorContext, SelectorKey } from '@/hooks/selectors/types'
|
||||
import type { WorkflowState } from '@/stores/workflows/workflow/types'
|
||||
|
||||
const logger = createLogger('ResolveValues')
|
||||
@@ -39,74 +40,8 @@ interface ResolutionContext {
|
||||
blockId?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Extended context extracted from block subBlocks for selector resolution
|
||||
*/
|
||||
interface ExtendedSelectorContext {
|
||||
credentialId?: string
|
||||
domain?: string
|
||||
projectId?: string
|
||||
planId?: string
|
||||
teamId?: string
|
||||
knowledgeBaseId?: string
|
||||
siteId?: string
|
||||
collectionId?: string
|
||||
spreadsheetId?: string
|
||||
baseId?: string
|
||||
datasetId?: string
|
||||
serviceDeskId?: string
|
||||
}
|
||||
|
||||
function getSemanticFallback(subBlockId: string, subBlockConfig?: SubBlockConfig): string {
|
||||
if (subBlockConfig?.title) {
|
||||
return subBlockConfig.title.toLowerCase()
|
||||
}
|
||||
|
||||
const patterns: Record<string, string> = {
|
||||
credential: 'credential',
|
||||
channel: 'channel',
|
||||
channelId: 'channel',
|
||||
user: 'user',
|
||||
userId: 'user',
|
||||
workflow: 'workflow',
|
||||
workflowId: 'workflow',
|
||||
file: 'file',
|
||||
fileId: 'file',
|
||||
folder: 'folder',
|
||||
folderId: 'folder',
|
||||
project: 'project',
|
||||
projectId: 'project',
|
||||
team: 'team',
|
||||
teamId: 'team',
|
||||
sheet: 'sheet',
|
||||
sheetId: 'sheet',
|
||||
document: 'document',
|
||||
documentId: 'document',
|
||||
knowledgeBase: 'knowledge base',
|
||||
knowledgeBaseId: 'knowledge base',
|
||||
server: 'server',
|
||||
serverId: 'server',
|
||||
tool: 'tool',
|
||||
toolId: 'tool',
|
||||
calendar: 'calendar',
|
||||
calendarId: 'calendar',
|
||||
label: 'label',
|
||||
labelId: 'label',
|
||||
site: 'site',
|
||||
siteId: 'site',
|
||||
collection: 'collection',
|
||||
collectionId: 'collection',
|
||||
item: 'item',
|
||||
itemId: 'item',
|
||||
contact: 'contact',
|
||||
contactId: 'contact',
|
||||
task: 'task',
|
||||
taskId: 'task',
|
||||
chat: 'chat',
|
||||
chatId: 'chat',
|
||||
}
|
||||
|
||||
return patterns[subBlockId] || 'value'
|
||||
function getSemanticFallback(subBlockConfig: SubBlockConfig): string {
|
||||
return (subBlockConfig.title ?? subBlockConfig.id).toLowerCase()
|
||||
}
|
||||
|
||||
async function resolveCredential(credentialId: string, workflowId: string): Promise<string | null> {
|
||||
@@ -150,26 +85,10 @@ async function resolveWorkflow(workflowId: string): Promise<string | null> {
|
||||
async function resolveSelectorValue(
|
||||
value: string,
|
||||
selectorKey: SelectorKey,
|
||||
extendedContext: ExtendedSelectorContext,
|
||||
workflowId: string
|
||||
selectorContext: SelectorContext
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
const definition = getSelectorDefinition(selectorKey)
|
||||
const selectorContext = {
|
||||
workflowId,
|
||||
credentialId: extendedContext.credentialId,
|
||||
domain: extendedContext.domain,
|
||||
projectId: extendedContext.projectId,
|
||||
planId: extendedContext.planId,
|
||||
teamId: extendedContext.teamId,
|
||||
knowledgeBaseId: extendedContext.knowledgeBaseId,
|
||||
siteId: extendedContext.siteId,
|
||||
collectionId: extendedContext.collectionId,
|
||||
spreadsheetId: extendedContext.spreadsheetId,
|
||||
baseId: extendedContext.baseId,
|
||||
datasetId: extendedContext.datasetId,
|
||||
serviceDeskId: extendedContext.serviceDeskId,
|
||||
}
|
||||
|
||||
if (definition.fetchById) {
|
||||
const result = await definition.fetchById({
|
||||
@@ -219,37 +138,14 @@ export function formatValueForDisplay(value: unknown): string {
|
||||
return String(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts extended context from a block's subBlocks for selector resolution.
|
||||
* This mirrors the context extraction done in the UI components.
|
||||
*/
|
||||
function extractExtendedContext(
|
||||
function extractSelectorContext(
|
||||
blockId: string,
|
||||
currentState: WorkflowState
|
||||
): ExtendedSelectorContext {
|
||||
currentState: WorkflowState,
|
||||
workflowId: string
|
||||
): SelectorContext {
|
||||
const block = currentState.blocks?.[blockId]
|
||||
if (!block?.subBlocks) return {}
|
||||
|
||||
const getStringValue = (id: string): string | undefined => {
|
||||
const subBlock = block.subBlocks[id] as { value?: unknown } | undefined
|
||||
const val = subBlock?.value
|
||||
return typeof val === 'string' ? val : undefined
|
||||
}
|
||||
|
||||
return {
|
||||
credentialId: getStringValue('credential'),
|
||||
domain: getStringValue('domain'),
|
||||
projectId: getStringValue('projectId'),
|
||||
planId: getStringValue('planId'),
|
||||
teamId: getStringValue('teamId'),
|
||||
knowledgeBaseId: getStringValue('knowledgeBaseId'),
|
||||
siteId: getStringValue('siteId'),
|
||||
collectionId: getStringValue('collectionId'),
|
||||
spreadsheetId: getStringValue('spreadsheetId') || getStringValue('fileId'),
|
||||
baseId: getStringValue('baseId') || getStringValue('baseSelector'),
|
||||
datasetId: getStringValue('datasetId') || getStringValue('datasetSelector'),
|
||||
serviceDeskId: getStringValue('serviceDeskId') || getStringValue('serviceDeskSelector'),
|
||||
}
|
||||
if (!block?.subBlocks) return { workflowId }
|
||||
return buildSelectorContextFromBlock(block.type, block.subBlocks, { workflowId })
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,11 +171,14 @@ export async function resolveValueForDisplay(
|
||||
|
||||
const blockConfig = getBlock(context.blockType)
|
||||
const subBlockConfig = blockConfig?.subBlocks.find((sb) => sb.id === context.subBlockId)
|
||||
const semanticFallback = getSemanticFallback(context.subBlockId, subBlockConfig)
|
||||
if (!subBlockConfig) {
|
||||
return { original: value, displayLabel: formatValueForDisplay(value), resolved: false }
|
||||
}
|
||||
const semanticFallback = getSemanticFallback(subBlockConfig)
|
||||
|
||||
const extendedContext = context.blockId
|
||||
? extractExtendedContext(context.blockId, context.currentState)
|
||||
: {}
|
||||
const selectorCtx = context.blockId
|
||||
? extractSelectorContext(context.blockId, context.currentState, context.workflowId)
|
||||
: { workflowId: context.workflowId }
|
||||
|
||||
// Credential fields (oauth-input or credential subBlockId)
|
||||
const isCredentialField =
|
||||
@@ -311,29 +210,10 @@ export async function resolveValueForDisplay(
|
||||
// Selector types that require hydration (file-selector, sheet-selector, etc.)
|
||||
// These support external service IDs like Google Drive file IDs
|
||||
if (subBlockConfig && SELECTOR_TYPES_HYDRATION_REQUIRED.includes(subBlockConfig.type)) {
|
||||
const resolution = resolveSelectorForSubBlock(subBlockConfig, {
|
||||
workflowId: context.workflowId,
|
||||
credentialId: extendedContext.credentialId,
|
||||
domain: extendedContext.domain,
|
||||
projectId: extendedContext.projectId,
|
||||
planId: extendedContext.planId,
|
||||
teamId: extendedContext.teamId,
|
||||
knowledgeBaseId: extendedContext.knowledgeBaseId,
|
||||
siteId: extendedContext.siteId,
|
||||
collectionId: extendedContext.collectionId,
|
||||
spreadsheetId: extendedContext.spreadsheetId,
|
||||
baseId: extendedContext.baseId,
|
||||
datasetId: extendedContext.datasetId,
|
||||
serviceDeskId: extendedContext.serviceDeskId,
|
||||
})
|
||||
const resolution = resolveSelectorForSubBlock(subBlockConfig, selectorCtx)
|
||||
|
||||
if (resolution?.key) {
|
||||
const label = await resolveSelectorValue(
|
||||
value,
|
||||
resolution.key,
|
||||
extendedContext,
|
||||
context.workflowId
|
||||
)
|
||||
const label = await resolveSelectorValue(value, resolution.key, selectorCtx)
|
||||
if (label) {
|
||||
return { original: value, displayLabel: label, resolved: true }
|
||||
}
|
||||
|
||||
125
apps/sim/lib/workflows/subblocks/context.test.ts
Normal file
125
apps/sim/lib/workflows/subblocks/context.test.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* @vitest-environment node
|
||||
*/
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.unmock('@/blocks/registry')
|
||||
|
||||
import { getAllBlocks } from '@/blocks/registry'
|
||||
import { buildSelectorContextFromBlock, SELECTOR_CONTEXT_FIELDS } from './context'
|
||||
import { buildCanonicalIndex, isCanonicalPair } from './visibility'
|
||||
|
||||
describe('buildSelectorContextFromBlock', () => {
|
||||
it('should extract knowledgeBaseId from knowledgeBaseSelector via canonical mapping', () => {
|
||||
const ctx = buildSelectorContextFromBlock('knowledge', {
|
||||
operation: { id: 'operation', type: 'dropdown', value: 'search' },
|
||||
knowledgeBaseSelector: {
|
||||
id: 'knowledgeBaseSelector',
|
||||
type: 'knowledge-base-selector',
|
||||
value: 'kb-uuid-123',
|
||||
},
|
||||
})
|
||||
|
||||
expect(ctx.knowledgeBaseId).toBe('kb-uuid-123')
|
||||
})
|
||||
|
||||
it('should extract knowledgeBaseId from manualKnowledgeBaseId via canonical mapping', () => {
|
||||
const ctx = buildSelectorContextFromBlock('knowledge', {
|
||||
operation: { id: 'operation', type: 'dropdown', value: 'search' },
|
||||
manualKnowledgeBaseId: {
|
||||
id: 'manualKnowledgeBaseId',
|
||||
type: 'short-input',
|
||||
value: 'manual-kb-id',
|
||||
},
|
||||
})
|
||||
|
||||
expect(ctx.knowledgeBaseId).toBe('manual-kb-id')
|
||||
})
|
||||
|
||||
it('should skip null/empty values', () => {
|
||||
const ctx = buildSelectorContextFromBlock('knowledge', {
|
||||
knowledgeBaseSelector: {
|
||||
id: 'knowledgeBaseSelector',
|
||||
type: 'knowledge-base-selector',
|
||||
value: '',
|
||||
},
|
||||
})
|
||||
|
||||
expect(ctx.knowledgeBaseId).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should return empty context for unknown block types', () => {
|
||||
const ctx = buildSelectorContextFromBlock('nonexistent_block', {
|
||||
foo: { id: 'foo', type: 'short-input', value: 'bar' },
|
||||
})
|
||||
|
||||
expect(ctx).toEqual({})
|
||||
})
|
||||
|
||||
it('should pass through workflowId from opts', () => {
|
||||
const ctx = buildSelectorContextFromBlock(
|
||||
'knowledge',
|
||||
{ operation: { id: 'operation', type: 'dropdown', value: 'search' } },
|
||||
{ workflowId: 'wf-123' }
|
||||
)
|
||||
|
||||
expect(ctx.workflowId).toBe('wf-123')
|
||||
})
|
||||
|
||||
it('should ignore subblock keys not in SELECTOR_CONTEXT_FIELDS', () => {
|
||||
const ctx = buildSelectorContextFromBlock('knowledge', {
|
||||
operation: { id: 'operation', type: 'dropdown', value: 'search' },
|
||||
query: { id: 'query', type: 'short-input', value: 'some search query' },
|
||||
})
|
||||
|
||||
expect((ctx as Record<string, unknown>).query).toBeUndefined()
|
||||
expect((ctx as Record<string, unknown>).operation).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('SELECTOR_CONTEXT_FIELDS validation', () => {
|
||||
it('every entry must be a canonicalParamId (if a canonical pair exists) or a direct subblock ID', () => {
|
||||
const allCanonicalParamIds = new Set<string>()
|
||||
const allSubBlockIds = new Set<string>()
|
||||
const idsInCanonicalPairs = new Set<string>()
|
||||
|
||||
for (const block of getAllBlocks()) {
|
||||
const index = buildCanonicalIndex(block.subBlocks)
|
||||
|
||||
for (const sb of block.subBlocks) {
|
||||
allSubBlockIds.add(sb.id)
|
||||
if (sb.canonicalParamId) {
|
||||
allCanonicalParamIds.add(sb.canonicalParamId)
|
||||
}
|
||||
}
|
||||
|
||||
for (const group of Object.values(index.groupsById)) {
|
||||
if (!isCanonicalPair(group)) continue
|
||||
if (group.basicId) idsInCanonicalPairs.add(group.basicId)
|
||||
for (const advId of group.advancedIds) idsInCanonicalPairs.add(advId)
|
||||
}
|
||||
}
|
||||
|
||||
const errors: string[] = []
|
||||
|
||||
for (const field of SELECTOR_CONTEXT_FIELDS) {
|
||||
const f = field as string
|
||||
if (allCanonicalParamIds.has(f)) continue
|
||||
|
||||
if (idsInCanonicalPairs.has(f)) {
|
||||
errors.push(
|
||||
`"${f}" is a member subblock ID inside a canonical pair — use the canonicalParamId instead`
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!allSubBlockIds.has(f)) {
|
||||
errors.push(`"${f}" is not a canonicalParamId or subblock ID in any block definition`)
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new Error(`SELECTOR_CONTEXT_FIELDS validation failed:\n${errors.join('\n')}`)
|
||||
}
|
||||
})
|
||||
})
|
||||
60
apps/sim/lib/workflows/subblocks/context.ts
Normal file
60
apps/sim/lib/workflows/subblocks/context.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { getBlock } from '@/blocks'
|
||||
import type { SelectorContext } from '@/hooks/selectors/types'
|
||||
import type { SubBlockState } from '@/stores/workflows/workflow/types'
|
||||
import { buildCanonicalIndex } from './visibility'
|
||||
|
||||
/**
|
||||
* Canonical param IDs (or raw subblock IDs) that correspond to SelectorContext fields.
|
||||
* A subblock's resolved canonical key is set on the context only if it appears here.
|
||||
*/
|
||||
export const SELECTOR_CONTEXT_FIELDS = new Set<keyof SelectorContext>([
|
||||
'oauthCredential',
|
||||
'domain',
|
||||
'teamId',
|
||||
'projectId',
|
||||
'knowledgeBaseId',
|
||||
'planId',
|
||||
'siteId',
|
||||
'collectionId',
|
||||
'spreadsheetId',
|
||||
'fileId',
|
||||
'baseId',
|
||||
'datasetId',
|
||||
'serviceDeskId',
|
||||
])
|
||||
|
||||
/**
|
||||
* Builds a SelectorContext from a block's subBlocks using the canonical index.
|
||||
*
|
||||
* Iterates all subblocks, resolves each through canonicalIdBySubBlockId to get
|
||||
* the canonical key, then checks it against SELECTOR_CONTEXT_FIELDS.
|
||||
* This avoids hardcoding subblock IDs and automatically handles basic/advanced
|
||||
* renames.
|
||||
*/
|
||||
export function buildSelectorContextFromBlock(
|
||||
blockType: string,
|
||||
subBlocks: Record<string, SubBlockState | { value?: unknown }>,
|
||||
opts?: { workflowId?: string }
|
||||
): SelectorContext {
|
||||
const context: SelectorContext = {}
|
||||
if (opts?.workflowId) context.workflowId = opts.workflowId
|
||||
|
||||
const blockConfig = getBlock(blockType)
|
||||
if (!blockConfig) return context
|
||||
|
||||
const canonicalIndex = buildCanonicalIndex(blockConfig.subBlocks)
|
||||
|
||||
for (const [subBlockId, subBlock] of Object.entries(subBlocks)) {
|
||||
const val = subBlock?.value
|
||||
if (val === null || val === undefined) continue
|
||||
const strValue = typeof val === 'string' ? val : String(val)
|
||||
if (!strValue) continue
|
||||
|
||||
const canonicalKey = canonicalIndex.canonicalIdBySubBlockId[subBlockId] ?? subBlockId
|
||||
if (SELECTOR_CONTEXT_FIELDS.has(canonicalKey as keyof SelectorContext)) {
|
||||
context[canonicalKey as keyof SelectorContext] = strValue
|
||||
}
|
||||
}
|
||||
|
||||
return context
|
||||
}
|
||||
Reference in New Issue
Block a user