mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 23:17:59 -05:00
improvement(selectors): make serviceId sole source of truth (#2128)
* improvement(serviceId): make serviceId sole source of truth * incorrect gmail service id * fix teams selectors * fix linkedin
This commit is contained in:
committed by
GitHub
parent
1e080e98e8
commit
d22b21c8d1
@@ -3,6 +3,7 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { Tooltip } from '@/components/emcn'
|
||||
import { getProviderIdFromServiceId } from '@/lib/oauth/oauth'
|
||||
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
|
||||
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
|
||||
import { useForeignCredential } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-foreign-credential'
|
||||
@@ -41,8 +42,11 @@ export function ChannelSelectorInput({
|
||||
const effectiveCredential = previewContextValues?.credential ?? connectedCredential
|
||||
const [_channelInfo, setChannelInfo] = useState<string | null>(null)
|
||||
|
||||
const provider = subBlock.provider || 'slack'
|
||||
const isSlack = provider === 'slack'
|
||||
// Use serviceId to identify the service and derive providerId for credential lookup
|
||||
const serviceId = subBlock.serviceId || ''
|
||||
const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId])
|
||||
const isSlack = serviceId === 'slack'
|
||||
|
||||
// Central dependsOn gating
|
||||
const { finalDisabled, dependsOn } = useDependsOnGate(blockId, subBlock, {
|
||||
disabled,
|
||||
@@ -58,7 +62,7 @@ export function ChannelSelectorInput({
|
||||
|
||||
// Determine if connected OAuth credential is foreign (not applicable for bot tokens)
|
||||
const { isForeignCredential } = useForeignCredential(
|
||||
'slack',
|
||||
effectiveProviderId,
|
||||
(effectiveAuthMethod as string) === 'bot_token' ? '' : (effectiveCredential as string) || ''
|
||||
)
|
||||
|
||||
@@ -87,11 +91,11 @@ export function ChannelSelectorInput({
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger asChild>
|
||||
<div className='w-full rounded border border-dashed p-4 text-center text-muted-foreground text-sm'>
|
||||
Channel selector not supported for provider: {provider}
|
||||
Channel selector not supported for service: {serviceId || 'unknown'}
|
||||
</div>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side='top'>
|
||||
<p>This channel selector is not yet implemented for {provider}</p>
|
||||
<p>This channel selector is not yet implemented for {serviceId || 'unknown'}</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
)
|
||||
|
||||
@@ -7,7 +7,6 @@ import { createLogger } from '@/lib/logs/console/logger'
|
||||
import {
|
||||
getCanonicalScopesForProvider,
|
||||
getProviderIdFromServiceId,
|
||||
getServiceIdFromScopes,
|
||||
OAUTH_PROVIDERS,
|
||||
type OAuthProvider,
|
||||
parseProvider,
|
||||
@@ -42,23 +41,19 @@ export function CredentialSelector({
|
||||
const { activeWorkflowId } = useWorkflowRegistry()
|
||||
const [storeValue, setStoreValue] = useSubBlockValue<string | null>(blockId, subBlock.id)
|
||||
|
||||
const provider = subBlock.provider as OAuthProvider
|
||||
const requiredScopes = subBlock.requiredScopes || []
|
||||
const label = subBlock.placeholder || 'Select credential'
|
||||
const serviceId = subBlock.serviceId
|
||||
const serviceId = subBlock.serviceId || ''
|
||||
|
||||
const effectiveValue = isPreview && previewValue !== undefined ? previewValue : storeValue
|
||||
const selectedId = typeof effectiveValue === 'string' ? effectiveValue : ''
|
||||
|
||||
const effectiveServiceId = useMemo(
|
||||
() => serviceId || getServiceIdFromScopes(provider, requiredScopes),
|
||||
[provider, requiredScopes, serviceId]
|
||||
)
|
||||
|
||||
// serviceId is now the canonical identifier - derive provider from it
|
||||
const effectiveProviderId = useMemo(
|
||||
() => getProviderIdFromServiceId(effectiveServiceId),
|
||||
[effectiveServiceId]
|
||||
() => getProviderIdFromServiceId(serviceId) as OAuthProvider,
|
||||
[serviceId]
|
||||
)
|
||||
const provider = effectiveProviderId
|
||||
|
||||
const {
|
||||
data: credentials = [],
|
||||
@@ -259,7 +254,7 @@ export function CredentialSelector({
|
||||
toolName={getProviderName(provider)}
|
||||
requiredScopes={getCanonicalScopesForProvider(effectiveProviderId)}
|
||||
newScopes={missingRequiredScopes}
|
||||
serviceId={effectiveServiceId}
|
||||
serviceId={serviceId}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { Tooltip } from '@/components/emcn'
|
||||
import { getProviderIdFromServiceId } from '@/lib/oauth'
|
||||
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
|
||||
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
|
||||
import { useForeignCredential } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-foreign-credential'
|
||||
@@ -59,10 +60,11 @@ export function FileSelectorInput({
|
||||
? ((connectedCredential as Record<string, any>).id ?? '')
|
||||
: ''
|
||||
|
||||
const { isForeignCredential } = useForeignCredential(
|
||||
subBlock.serviceId || subBlock.provider,
|
||||
normalizedCredentialId
|
||||
)
|
||||
// Derive provider from serviceId using OAuth config (same pattern as credential-selector)
|
||||
const serviceId = subBlock.serviceId || ''
|
||||
const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId])
|
||||
|
||||
const { isForeignCredential } = useForeignCredential(effectiveProviderId, normalizedCredentialId)
|
||||
|
||||
const selectorResolution = useMemo<SelectorResolution | null>(() => {
|
||||
return resolveSelectorForSubBlock(subBlock, {
|
||||
@@ -109,11 +111,11 @@ export function FileSelectorInput({
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger asChild>
|
||||
<div className='w-full rounded border border-dashed p-4 text-center text-muted-foreground text-sm'>
|
||||
File selector not supported for provider: {subBlock.provider || subBlock.serviceId}
|
||||
File selector not supported for service: {serviceId || 'unknown'}
|
||||
</div>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side='top'>
|
||||
<p>This file selector is not implemented for {subBlock.provider || subBlock.serviceId}</p>
|
||||
<p>This file selector is not implemented for {serviceId || 'unknown'}</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { getProviderIdFromServiceId } from '@/lib/oauth'
|
||||
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
|
||||
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
|
||||
import { useForeignCredential } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-foreign-credential'
|
||||
@@ -30,14 +31,18 @@ export function FolderSelectorInput({
|
||||
const { collaborativeSetSubblockValue } = useCollaborativeWorkflow()
|
||||
const { activeWorkflowId } = useWorkflowRegistry()
|
||||
const [selectedFolderId, setSelectedFolderId] = useState<string>('')
|
||||
const providerKey = (subBlock.provider ?? subBlock.serviceId ?? '').toLowerCase()
|
||||
const credentialProvider = subBlock.serviceId ?? subBlock.provider
|
||||
|
||||
// Derive provider from serviceId using OAuth config (same pattern as credential-selector)
|
||||
const serviceId = subBlock.serviceId || ''
|
||||
const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId])
|
||||
const providerKey = serviceId.toLowerCase()
|
||||
|
||||
const isCopyDestinationSelector =
|
||||
subBlock.canonicalParamId === 'copyDestinationId' ||
|
||||
subBlock.id === 'copyDestinationFolder' ||
|
||||
subBlock.id === 'manualCopyDestinationFolder'
|
||||
const { isForeignCredential } = useForeignCredential(
|
||||
credentialProvider,
|
||||
effectiveProviderId,
|
||||
(connectedCredential as string) || ''
|
||||
)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { Tooltip } from '@/components/emcn'
|
||||
import { getProviderIdFromServiceId } from '@/lib/oauth'
|
||||
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
|
||||
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
|
||||
import { useForeignCredential } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-foreign-credential'
|
||||
@@ -46,8 +47,13 @@ export function ProjectSelectorInput({
|
||||
const linearTeamId = previewContextValues?.teamId ?? linearTeamIdFromStore
|
||||
const jiraDomain = previewContextValues?.domain ?? jiraDomainFromStore
|
||||
|
||||
// Derive provider from serviceId using OAuth config
|
||||
const serviceId = subBlock.serviceId || ''
|
||||
const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId])
|
||||
const isLinear = serviceId === 'linear'
|
||||
|
||||
const { isForeignCredential } = useForeignCredential(
|
||||
subBlock.provider || subBlock.serviceId || 'jira',
|
||||
effectiveProviderId,
|
||||
(connectedCredential as string) || ''
|
||||
)
|
||||
const activeWorkflowId = useWorkflowRegistry((s) => s.activeWorkflowId) as string | null
|
||||
@@ -58,10 +64,6 @@ export function ProjectSelectorInput({
|
||||
previewContextValues,
|
||||
})
|
||||
|
||||
// Get provider-specific values
|
||||
const provider = subBlock.provider || 'jira'
|
||||
const isLinear = provider === 'linear'
|
||||
|
||||
// Jira/Discord upstream fields - use values from previewContextValues or store
|
||||
const jiraCredential = connectedCredential
|
||||
const domain = (jiraDomain as string) || ''
|
||||
@@ -121,7 +123,7 @@ export function ProjectSelectorInput({
|
||||
/>
|
||||
) : (
|
||||
<div className='w-full rounded border border-dashed p-4 text-center text-muted-foreground text-sm'>
|
||||
Project selector not supported for provider: {subBlock.provider || 'unknown'}
|
||||
Project selector not supported for service: {serviceId}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Toggle } from '@/components/ui/toggle'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import {
|
||||
getCanonicalScopesForProvider,
|
||||
getProviderIdFromServiceId,
|
||||
type OAuthProvider,
|
||||
type OAuthService,
|
||||
} from '@/lib/oauth/oauth'
|
||||
@@ -168,7 +169,6 @@ function FileSelectorSyncWrapper({
|
||||
id: paramId,
|
||||
type: 'file-selector' as const,
|
||||
title: paramId,
|
||||
provider: uiComponent.provider,
|
||||
serviceId: uiComponent.serviceId,
|
||||
mimeType: uiComponent.mimeType,
|
||||
requiredScopes: uiComponent.requiredScopes || [],
|
||||
@@ -467,7 +467,7 @@ function ChannelSelectorSyncWrapper({
|
||||
id: paramId,
|
||||
type: 'channel-selector' as const,
|
||||
title: paramId,
|
||||
provider: uiComponent.provider || 'slack',
|
||||
serviceId: uiComponent.serviceId,
|
||||
placeholder: uiComponent.placeholder,
|
||||
dependsOn: uiComponent.dependsOn,
|
||||
}}
|
||||
@@ -1404,7 +1404,6 @@ export function ToolInput({
|
||||
id: `tool-${toolIndex || 0}-${param.id}`,
|
||||
type: 'project-selector' as const,
|
||||
title: param.id,
|
||||
provider: uiComponent.provider || 'jira',
|
||||
serviceId: uiComponent.serviceId,
|
||||
placeholder: uiComponent.placeholder,
|
||||
requiredScopes: uiComponent.requiredScopes,
|
||||
@@ -1421,7 +1420,7 @@ export function ToolInput({
|
||||
<ToolCredentialSelector
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
provider={(uiComponent.provider || uiComponent.serviceId) as OAuthProvider}
|
||||
provider={getProviderIdFromServiceId(uiComponent.serviceId || '') as OAuthProvider}
|
||||
serviceId={uiComponent.serviceId as OAuthService}
|
||||
disabled={disabled}
|
||||
requiredScopes={uiComponent.requiredScopes || []}
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { Tooltip } from '@/components/emcn'
|
||||
import { getProviderIdFromServiceId } from '@/lib/oauth'
|
||||
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
|
||||
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
|
||||
import { useForeignCredential } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-foreign-credential'
|
||||
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
|
||||
import type { SubBlockConfig } from '@/blocks/types'
|
||||
import type { SelectorContext, SelectorKey } from '@/hooks/selectors/types'
|
||||
import { resolveSelectorForSubBlock, type SelectorResolution } from '@/hooks/selectors/resolution'
|
||||
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
|
||||
@@ -59,27 +60,23 @@ export function FileSelectorInput({
|
||||
? ((connectedCredential as Record<string, any>).id ?? '')
|
||||
: ''
|
||||
|
||||
const { isForeignCredential } = useForeignCredential(
|
||||
subBlock.provider || subBlock.serviceId || 'google-drive',
|
||||
normalizedCredentialId
|
||||
)
|
||||
// Derive provider from serviceId using OAuth config
|
||||
const serviceId = subBlock.serviceId || ''
|
||||
const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId])
|
||||
|
||||
const selectorResolution = useMemo(() => {
|
||||
return resolveSelector({
|
||||
provider: subBlock.provider || '',
|
||||
serviceId: subBlock.serviceId,
|
||||
mimeType: subBlock.mimeType,
|
||||
const { isForeignCredential } = useForeignCredential(effectiveProviderId, normalizedCredentialId)
|
||||
|
||||
const selectorResolution = useMemo<SelectorResolution | null>(() => {
|
||||
return resolveSelectorForSubBlock(subBlock, {
|
||||
credentialId: normalizedCredentialId,
|
||||
workflowId: workflowIdFromUrl,
|
||||
domain: (domainValue as string) || '',
|
||||
projectId: (projectIdValue as string) || '',
|
||||
planId: (planIdValue as string) || '',
|
||||
teamId: (teamIdValue as string) || '',
|
||||
domain: (domainValue as string) || undefined,
|
||||
projectId: (projectIdValue as string) || undefined,
|
||||
planId: (planIdValue as string) || undefined,
|
||||
teamId: (teamIdValue as string) || undefined,
|
||||
})
|
||||
}, [
|
||||
subBlock.provider,
|
||||
subBlock.serviceId,
|
||||
subBlock.mimeType,
|
||||
subBlock,
|
||||
normalizedCredentialId,
|
||||
workflowIdFromUrl,
|
||||
domainValue,
|
||||
@@ -90,15 +87,15 @@ export function FileSelectorInput({
|
||||
|
||||
const missingCredential = !normalizedCredentialId
|
||||
const missingDomain =
|
||||
selectorResolution.key &&
|
||||
selectorResolution?.key &&
|
||||
(selectorResolution.key === 'confluence.pages' || selectorResolution.key === 'jira.issues') &&
|
||||
!selectorResolution.context.domain
|
||||
const missingProject =
|
||||
selectorResolution.key === 'jira.issues' &&
|
||||
selectorResolution?.key === 'jira.issues' &&
|
||||
subBlock.dependsOn?.includes('projectId') &&
|
||||
!selectorResolution.context.projectId
|
||||
!selectorResolution?.context.projectId
|
||||
const missingPlan =
|
||||
selectorResolution.key === 'microsoft.planner' && !selectorResolution.context.planId
|
||||
selectorResolution?.key === 'microsoft.planner' && !selectorResolution?.context.planId
|
||||
|
||||
const disabledReason =
|
||||
finalDisabled ||
|
||||
@@ -107,18 +104,18 @@ export function FileSelectorInput({
|
||||
missingDomain ||
|
||||
missingProject ||
|
||||
missingPlan ||
|
||||
selectorResolution.key === null
|
||||
!selectorResolution?.key
|
||||
|
||||
if (selectorResolution.key === null) {
|
||||
if (!selectorResolution?.key) {
|
||||
return (
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger asChild>
|
||||
<div className='w-full rounded border border-dashed p-4 text-center text-muted-foreground text-sm'>
|
||||
File selector not supported for provider: {subBlock.provider || subBlock.serviceId}
|
||||
File selector not supported for service: {serviceId}
|
||||
</div>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side='top'>
|
||||
<p>This file selector is not implemented for {subBlock.provider || subBlock.serviceId}</p>
|
||||
<p>This file selector is not implemented for {serviceId}</p>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
)
|
||||
@@ -143,69 +140,3 @@ export function FileSelectorInput({
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
interface SelectorParams {
|
||||
provider: string
|
||||
serviceId?: string
|
||||
mimeType?: string
|
||||
credentialId: string
|
||||
workflowId: string
|
||||
domain?: string
|
||||
projectId?: string
|
||||
planId?: string
|
||||
teamId?: string
|
||||
}
|
||||
|
||||
function resolveSelector(params: SelectorParams): {
|
||||
key: SelectorKey | null
|
||||
context: SelectorContext
|
||||
allowSearch: boolean
|
||||
} {
|
||||
const baseContext: SelectorContext = {
|
||||
credentialId: params.credentialId,
|
||||
workflowId: params.workflowId,
|
||||
domain: params.domain,
|
||||
projectId: params.projectId,
|
||||
planId: params.planId,
|
||||
teamId: params.teamId,
|
||||
mimeType: params.mimeType,
|
||||
}
|
||||
|
||||
switch (params.provider) {
|
||||
case 'google-calendar':
|
||||
return { key: 'google.calendar', context: baseContext, allowSearch: false }
|
||||
case 'confluence':
|
||||
return { key: 'confluence.pages', context: baseContext, allowSearch: true }
|
||||
case 'jira':
|
||||
return { key: 'jira.issues', context: baseContext, allowSearch: true }
|
||||
case 'microsoft-teams':
|
||||
return { key: 'microsoft.teams', context: baseContext, allowSearch: true }
|
||||
case 'wealthbox':
|
||||
return { key: 'wealthbox.contacts', context: baseContext, allowSearch: true }
|
||||
case 'microsoft-planner':
|
||||
return { key: 'microsoft.planner', context: baseContext, allowSearch: true }
|
||||
case 'microsoft-excel':
|
||||
return { key: 'microsoft.excel', context: baseContext, allowSearch: true }
|
||||
case 'microsoft-word':
|
||||
return { key: 'microsoft.word', context: baseContext, allowSearch: true }
|
||||
case 'google-drive':
|
||||
return { key: 'google.drive', context: baseContext, allowSearch: true }
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if (params.serviceId === 'onedrive') {
|
||||
const key: SelectorKey = params.mimeType === 'file' ? 'onedrive.files' : 'onedrive.folders'
|
||||
return { key, context: baseContext, allowSearch: true }
|
||||
}
|
||||
|
||||
if (params.serviceId === 'sharepoint') {
|
||||
return { key: 'sharepoint.sites', context: baseContext, allowSearch: true }
|
||||
}
|
||||
|
||||
if (params.serviceId === 'google-drive') {
|
||||
return { key: 'google.drive', context: baseContext, allowSearch: true }
|
||||
}
|
||||
|
||||
return { key: null, context: baseContext, allowSearch: true }
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Tooltip } from '@/components/emcn/components/tooltip/tooltip'
|
||||
import { getEnv, isTruthy } from '@/lib/env'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { createMcpToolId } from '@/lib/mcp/utils'
|
||||
import { getProviderIdFromServiceId } from '@/lib/oauth'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
|
||||
import {
|
||||
@@ -272,9 +273,12 @@ const SubBlockRow = ({
|
||||
|
||||
const credentialSourceId =
|
||||
subBlock?.type === 'oauth-input' && typeof rawValue === 'string' ? rawValue : undefined
|
||||
const credentialProviderId = subBlock?.serviceId
|
||||
? getProviderIdFromServiceId(subBlock.serviceId)
|
||||
: undefined
|
||||
const { displayName: credentialName } = useCredentialName(
|
||||
credentialSourceId,
|
||||
subBlock?.provider,
|
||||
credentialProviderId,
|
||||
workflowId
|
||||
)
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ export const AirtableBlock: BlockConfig<AirtableResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Airtable Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'airtable',
|
||||
serviceId: 'airtable',
|
||||
requiredScopes: [
|
||||
'data.records:read',
|
||||
|
||||
@@ -34,7 +34,6 @@ export const AsanaBlock: BlockConfig<AsanaResponse> = {
|
||||
type: 'oauth-input',
|
||||
|
||||
required: true,
|
||||
provider: 'asana',
|
||||
serviceId: 'asana',
|
||||
requiredScopes: ['default'],
|
||||
placeholder: 'Select Asana account',
|
||||
|
||||
@@ -48,7 +48,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Confluence Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'confluence',
|
||||
serviceId: 'confluence',
|
||||
requiredScopes: [
|
||||
'read:confluence-content.all',
|
||||
@@ -82,7 +81,6 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
|
||||
title: 'Select Page',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'pageId',
|
||||
provider: 'confluence',
|
||||
serviceId: 'confluence',
|
||||
placeholder: 'Select Confluence page',
|
||||
dependsOn: ['credential', 'domain'],
|
||||
|
||||
@@ -71,7 +71,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter Discord server ID',
|
||||
required: true,
|
||||
provider: 'discord',
|
||||
serviceId: 'discord',
|
||||
},
|
||||
// Channel ID - for operations that need it
|
||||
@@ -81,7 +80,6 @@ export const DiscordBlock: BlockConfig<DiscordResponse> = {
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter Discord channel ID',
|
||||
required: true,
|
||||
provider: 'discord',
|
||||
serviceId: 'discord',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
|
||||
@@ -43,7 +43,6 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Gmail Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'google-email',
|
||||
serviceId: 'gmail',
|
||||
requiredScopes: [
|
||||
'https://www.googleapis.com/auth/gmail.send',
|
||||
@@ -157,7 +156,6 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
title: 'Label',
|
||||
type: 'folder-selector',
|
||||
canonicalParamId: 'folder',
|
||||
provider: 'google-email',
|
||||
serviceId: 'gmail',
|
||||
requiredScopes: ['https://www.googleapis.com/auth/gmail.labels'],
|
||||
placeholder: 'Select Gmail label/folder',
|
||||
@@ -232,7 +230,6 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
title: 'Move To Label',
|
||||
type: 'folder-selector',
|
||||
canonicalParamId: 'addLabelIds',
|
||||
provider: 'google-email',
|
||||
serviceId: 'gmail',
|
||||
requiredScopes: ['https://www.googleapis.com/auth/gmail.labels'],
|
||||
placeholder: 'Select destination label',
|
||||
@@ -258,7 +255,6 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
title: 'Remove From Label',
|
||||
type: 'folder-selector',
|
||||
canonicalParamId: 'removeLabelIds',
|
||||
provider: 'google-email',
|
||||
serviceId: 'gmail',
|
||||
requiredScopes: ['https://www.googleapis.com/auth/gmail.labels'],
|
||||
placeholder: 'Select label to remove',
|
||||
@@ -311,7 +307,6 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
|
||||
title: 'Label',
|
||||
type: 'folder-selector',
|
||||
canonicalParamId: 'labelIds',
|
||||
provider: 'google-email',
|
||||
serviceId: 'gmail',
|
||||
requiredScopes: ['https://www.googleapis.com/auth/gmail.labels'],
|
||||
placeholder: 'Select label',
|
||||
|
||||
@@ -33,7 +33,6 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
|
||||
title: 'Google Calendar Account',
|
||||
type: 'oauth-input',
|
||||
required: true,
|
||||
provider: 'google-calendar',
|
||||
serviceId: 'google-calendar',
|
||||
requiredScopes: ['https://www.googleapis.com/auth/calendar'],
|
||||
placeholder: 'Select Google Calendar account',
|
||||
@@ -44,7 +43,6 @@ export const GoogleCalendarBlock: BlockConfig<GoogleCalendarResponse> = {
|
||||
title: 'Calendar',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'calendarId',
|
||||
provider: 'google-calendar',
|
||||
serviceId: 'google-calendar',
|
||||
requiredScopes: ['https://www.googleapis.com/auth/calendar'],
|
||||
placeholder: 'Select calendar',
|
||||
|
||||
@@ -33,7 +33,6 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
|
||||
title: 'Google Account',
|
||||
type: 'oauth-input',
|
||||
required: true,
|
||||
provider: 'google-docs',
|
||||
serviceId: 'google-docs',
|
||||
requiredScopes: [
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
@@ -47,7 +46,6 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
|
||||
title: 'Select Document',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'documentId',
|
||||
provider: 'google-docs',
|
||||
serviceId: 'google-docs',
|
||||
requiredScopes: [],
|
||||
mimeType: 'application/vnd.google-apps.document',
|
||||
@@ -82,7 +80,6 @@ export const GoogleDocsBlock: BlockConfig<GoogleDocsResponse> = {
|
||||
title: 'Select Parent Folder',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'folderId',
|
||||
provider: 'google-docs',
|
||||
serviceId: 'google-docs',
|
||||
requiredScopes: [],
|
||||
mimeType: 'application/vnd.google-apps.folder',
|
||||
|
||||
@@ -34,7 +34,6 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
title: 'Google Drive Account',
|
||||
type: 'oauth-input',
|
||||
required: true,
|
||||
provider: 'google-drive',
|
||||
serviceId: 'google-drive',
|
||||
requiredScopes: [
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
@@ -104,7 +103,6 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
title: 'Select Parent Folder',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'folderId',
|
||||
provider: 'google-drive',
|
||||
serviceId: 'google-drive',
|
||||
requiredScopes: [
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
@@ -177,7 +175,6 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
title: 'Select Parent Folder',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'folderId',
|
||||
provider: 'google-drive',
|
||||
serviceId: 'google-drive',
|
||||
requiredScopes: [
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
@@ -205,7 +202,6 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
title: 'Select Folder',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'folderId',
|
||||
provider: 'google-drive',
|
||||
serviceId: 'google-drive',
|
||||
requiredScopes: [
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
@@ -247,7 +243,6 @@ export const GoogleDriveBlock: BlockConfig<GoogleDriveResponse> = {
|
||||
title: 'Select File',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'fileId',
|
||||
provider: 'google-drive',
|
||||
serviceId: 'google-drive',
|
||||
requiredScopes: [
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
|
||||
@@ -18,7 +18,6 @@ export const GoogleFormsBlock: BlockConfig = {
|
||||
title: 'Google Account',
|
||||
type: 'oauth-input',
|
||||
required: true,
|
||||
provider: 'google-forms',
|
||||
serviceId: 'google-forms',
|
||||
requiredScopes: [
|
||||
'https://www.googleapis.com/auth/userinfo.email',
|
||||
|
||||
@@ -34,7 +34,6 @@ export const GoogleSheetsBlock: BlockConfig<GoogleSheetsResponse> = {
|
||||
title: 'Google Account',
|
||||
type: 'oauth-input',
|
||||
required: true,
|
||||
provider: 'google-sheets',
|
||||
serviceId: 'google-sheets',
|
||||
requiredScopes: [
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
@@ -48,7 +47,6 @@ export const GoogleSheetsBlock: BlockConfig<GoogleSheetsResponse> = {
|
||||
title: 'Select Sheet',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'spreadsheetId',
|
||||
provider: 'google-sheets',
|
||||
serviceId: 'google-sheets',
|
||||
requiredScopes: [
|
||||
'https://www.googleapis.com/auth/drive.file',
|
||||
|
||||
@@ -35,7 +35,6 @@ export const GoogleVaultBlock: BlockConfig = {
|
||||
title: 'Google Vault Account',
|
||||
type: 'oauth-input',
|
||||
required: true,
|
||||
provider: 'google-vault',
|
||||
serviceId: 'google-vault',
|
||||
requiredScopes: [
|
||||
'https://www.googleapis.com/auth/ediscovery',
|
||||
|
||||
@@ -39,7 +39,6 @@ export const HubSpotBlock: BlockConfig<HubSpotResponse> = {
|
||||
id: 'credential',
|
||||
title: 'HubSpot Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'hubspot',
|
||||
serviceId: 'hubspot',
|
||||
requiredScopes: [
|
||||
'crm.objects.contacts.read',
|
||||
|
||||
@@ -58,7 +58,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
title: 'Jira Account',
|
||||
type: 'oauth-input',
|
||||
required: true,
|
||||
provider: 'jira',
|
||||
serviceId: 'jira',
|
||||
requiredScopes: [
|
||||
'read:jira-work',
|
||||
@@ -100,7 +99,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
title: 'Select Project',
|
||||
type: 'project-selector',
|
||||
canonicalParamId: 'projectId',
|
||||
provider: 'jira',
|
||||
serviceId: 'jira',
|
||||
placeholder: 'Select Jira project',
|
||||
dependsOn: ['credential', 'domain'],
|
||||
@@ -122,7 +120,6 @@ export const JiraBlock: BlockConfig<JiraResponse> = {
|
||||
title: 'Select Issue',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'issueKey',
|
||||
provider: 'jira',
|
||||
serviceId: 'jira',
|
||||
placeholder: 'Select Jira issue',
|
||||
dependsOn: ['credential', 'domain', 'projectId'],
|
||||
|
||||
@@ -129,7 +129,6 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Linear Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'linear',
|
||||
serviceId: 'linear',
|
||||
requiredScopes: ['read', 'write'],
|
||||
placeholder: 'Select Linear account',
|
||||
@@ -141,7 +140,6 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
|
||||
title: 'Team',
|
||||
type: 'project-selector',
|
||||
canonicalParamId: 'teamId',
|
||||
provider: 'linear',
|
||||
serviceId: 'linear',
|
||||
placeholder: 'Select a team',
|
||||
dependsOn: ['credential'],
|
||||
@@ -218,7 +216,6 @@ export const LinearBlock: BlockConfig<LinearResponse> = {
|
||||
title: 'Project',
|
||||
type: 'project-selector',
|
||||
canonicalParamId: 'projectId',
|
||||
provider: 'linear',
|
||||
serviceId: 'linear',
|
||||
placeholder: 'Select a project',
|
||||
dependsOn: ['credential', 'teamId'],
|
||||
|
||||
@@ -32,7 +32,6 @@ export const LinkedInBlock: BlockConfig<LinkedInResponse> = {
|
||||
id: 'credential',
|
||||
title: 'LinkedIn Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'linkedin',
|
||||
serviceId: 'linkedin',
|
||||
requiredScopes: ['profile', 'openid', 'email', 'w_member_social'],
|
||||
placeholder: 'Select LinkedIn account',
|
||||
|
||||
@@ -31,7 +31,6 @@ export const MicrosoftExcelBlock: BlockConfig<MicrosoftExcelResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Microsoft Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'microsoft-excel',
|
||||
serviceId: 'microsoft-excel',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
@@ -49,7 +48,6 @@ export const MicrosoftExcelBlock: BlockConfig<MicrosoftExcelResponse> = {
|
||||
title: 'Select Sheet',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'spreadsheetId',
|
||||
provider: 'microsoft-excel',
|
||||
serviceId: 'microsoft-excel',
|
||||
requiredScopes: [],
|
||||
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
|
||||
@@ -61,7 +61,6 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Microsoft Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'microsoft-planner',
|
||||
serviceId: 'microsoft-planner',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
@@ -94,7 +93,7 @@ export const MicrosoftPlannerBlock: BlockConfig<MicrosoftPlannerResponse> = {
|
||||
title: 'Task ID',
|
||||
type: 'file-selector',
|
||||
placeholder: 'Select a task',
|
||||
provider: 'microsoft-planner',
|
||||
serviceId: 'microsoft-planner',
|
||||
condition: { field: 'operation', value: ['read_task'] },
|
||||
dependsOn: ['credential', 'planId'],
|
||||
mode: 'basic',
|
||||
|
||||
@@ -43,7 +43,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Microsoft Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'microsoft-teams',
|
||||
serviceId: 'microsoft-teams',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
@@ -75,7 +74,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
title: 'Select Team',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'teamId',
|
||||
provider: 'microsoft-teams',
|
||||
serviceId: 'microsoft-teams',
|
||||
requiredScopes: [],
|
||||
placeholder: 'Select a team',
|
||||
@@ -119,7 +117,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
title: 'Select Chat',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'chatId',
|
||||
provider: 'microsoft-teams',
|
||||
serviceId: 'microsoft-teams',
|
||||
requiredScopes: [],
|
||||
placeholder: 'Select a chat',
|
||||
@@ -147,7 +144,6 @@ export const MicrosoftTeamsBlock: BlockConfig<MicrosoftTeamsResponse> = {
|
||||
title: 'Select Channel',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'channelId',
|
||||
provider: 'microsoft-teams',
|
||||
serviceId: 'microsoft-teams',
|
||||
requiredScopes: [],
|
||||
placeholder: 'Select a channel',
|
||||
|
||||
@@ -34,7 +34,6 @@ export const NotionBlock: BlockConfig<NotionResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Notion Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'notion',
|
||||
serviceId: 'notion',
|
||||
requiredScopes: ['workspace.content', 'workspace.name', 'page.read', 'page.write'],
|
||||
placeholder: 'Select Notion account',
|
||||
|
||||
@@ -38,7 +38,6 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Microsoft Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'onedrive',
|
||||
serviceId: 'onedrive',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
@@ -144,7 +143,6 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
title: 'Select Parent Folder',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'folderId',
|
||||
provider: 'microsoft',
|
||||
serviceId: 'onedrive',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
@@ -182,7 +180,6 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
title: 'Select Parent Folder',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'folderId',
|
||||
provider: 'microsoft',
|
||||
serviceId: 'onedrive',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
@@ -215,7 +212,6 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
title: 'Select Folder',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'folderId',
|
||||
provider: 'microsoft',
|
||||
serviceId: 'onedrive',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
@@ -262,7 +258,6 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
title: 'Select File',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'fileId',
|
||||
provider: 'microsoft',
|
||||
serviceId: 'onedrive',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
@@ -302,7 +297,6 @@ export const OneDriveBlock: BlockConfig<OneDriveResponse> = {
|
||||
title: 'Select File to Delete',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'fileId',
|
||||
provider: 'microsoft',
|
||||
serviceId: 'onedrive',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
|
||||
@@ -38,7 +38,6 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Microsoft Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'outlook',
|
||||
serviceId: 'outlook',
|
||||
requiredScopes: [
|
||||
'Mail.ReadWrite',
|
||||
@@ -175,7 +174,6 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
title: 'Folder',
|
||||
type: 'folder-selector',
|
||||
canonicalParamId: 'folder',
|
||||
provider: 'outlook',
|
||||
serviceId: 'outlook',
|
||||
requiredScopes: ['Mail.ReadWrite', 'Mail.ReadBasic', 'Mail.Read'],
|
||||
placeholder: 'Select Outlook folder',
|
||||
@@ -221,7 +219,6 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
title: 'Move To Folder',
|
||||
type: 'folder-selector',
|
||||
canonicalParamId: 'destinationId',
|
||||
provider: 'outlook',
|
||||
serviceId: 'outlook',
|
||||
requiredScopes: ['Mail.ReadWrite', 'Mail.ReadBasic', 'Mail.Read'],
|
||||
placeholder: 'Select destination folder',
|
||||
@@ -268,7 +265,6 @@ export const OutlookBlock: BlockConfig<OutlookResponse> = {
|
||||
title: 'Copy To Folder',
|
||||
type: 'folder-selector',
|
||||
canonicalParamId: 'copyDestinationId',
|
||||
provider: 'outlook',
|
||||
serviceId: 'outlook',
|
||||
requiredScopes: ['Mail.ReadWrite', 'Mail.ReadBasic', 'Mail.Read'],
|
||||
placeholder: 'Select destination folder',
|
||||
|
||||
@@ -45,7 +45,6 @@ export const PipedriveBlock: BlockConfig<PipedriveResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Pipedrive Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'pipedrive',
|
||||
serviceId: 'pipedrive',
|
||||
requiredScopes: [
|
||||
'base',
|
||||
|
||||
@@ -42,7 +42,6 @@ export const RedditBlock: BlockConfig<RedditResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Reddit Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'reddit',
|
||||
serviceId: 'reddit',
|
||||
requiredScopes: [
|
||||
'identity',
|
||||
|
||||
@@ -51,7 +51,6 @@ export const SalesforceBlock: BlockConfig<SalesforceResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Salesforce Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'salesforce',
|
||||
serviceId: 'salesforce',
|
||||
requiredScopes: ['api', 'refresh_token', 'openid'],
|
||||
placeholder: 'Select Salesforce account',
|
||||
|
||||
@@ -37,7 +37,6 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Microsoft Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'sharepoint',
|
||||
serviceId: 'sharepoint',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
@@ -56,7 +55,6 @@ export const SharepointBlock: BlockConfig<SharepointResponse> = {
|
||||
title: 'Select Site',
|
||||
type: 'file-selector',
|
||||
canonicalParamId: 'siteId',
|
||||
provider: 'microsoft',
|
||||
serviceId: 'sharepoint',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
|
||||
@@ -48,7 +48,6 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Slack Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'slack',
|
||||
serviceId: 'slack',
|
||||
requiredScopes: [
|
||||
'channels:read',
|
||||
@@ -85,7 +84,7 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
|
||||
title: 'Channel',
|
||||
type: 'channel-selector',
|
||||
canonicalParamId: 'channel',
|
||||
provider: 'slack',
|
||||
serviceId: 'slack',
|
||||
placeholder: 'Select Slack channel',
|
||||
mode: 'basic',
|
||||
dependsOn: ['credential', 'authMethod'],
|
||||
|
||||
@@ -41,7 +41,6 @@ export const TrelloBlock: BlockConfig<ToolResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Trello Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'trello',
|
||||
serviceId: 'trello',
|
||||
requiredScopes: ['read', 'write'],
|
||||
placeholder: 'Select Trello account',
|
||||
|
||||
@@ -33,7 +33,6 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Wealthbox Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'wealthbox',
|
||||
serviceId: 'wealthbox',
|
||||
requiredScopes: ['login', 'data'],
|
||||
placeholder: 'Select Wealthbox account',
|
||||
@@ -50,7 +49,6 @@ export const WealthboxBlock: BlockConfig<WealthboxResponse> = {
|
||||
id: 'contactId',
|
||||
title: 'Select Contact',
|
||||
type: 'file-selector',
|
||||
provider: 'wealthbox',
|
||||
serviceId: 'wealthbox',
|
||||
requiredScopes: ['login', 'data'],
|
||||
placeholder: 'Enter Contact ID',
|
||||
|
||||
@@ -34,7 +34,6 @@ export const WebflowBlock: BlockConfig<WebflowResponse> = {
|
||||
id: 'credential',
|
||||
title: 'Webflow Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'webflow',
|
||||
serviceId: 'webflow',
|
||||
requiredScopes: ['sites:read', 'sites:write', 'cms:read', 'cms:write'],
|
||||
placeholder: 'Select Webflow account',
|
||||
|
||||
@@ -90,7 +90,6 @@ export const WebhookBlock: BlockConfig = {
|
||||
id: 'gmailCredential',
|
||||
title: 'Gmail Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'google-email',
|
||||
serviceId: 'gmail',
|
||||
requiredScopes: [
|
||||
'https://www.googleapis.com/auth/gmail.modify',
|
||||
@@ -104,7 +103,6 @@ export const WebhookBlock: BlockConfig = {
|
||||
id: 'outlookCredential',
|
||||
title: 'Microsoft Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'outlook',
|
||||
serviceId: 'outlook',
|
||||
requiredScopes: [
|
||||
'Mail.ReadWrite',
|
||||
|
||||
@@ -31,7 +31,6 @@ export const XBlock: BlockConfig<XResponse> = {
|
||||
id: 'credential',
|
||||
title: 'X Account',
|
||||
type: 'oauth-input',
|
||||
provider: 'x',
|
||||
serviceId: 'x',
|
||||
requiredScopes: ['tweet.read', 'tweet.write', 'users.read', 'offline.access'],
|
||||
placeholder: 'Select X account',
|
||||
|
||||
@@ -222,8 +222,7 @@ export interface SubBlockConfig {
|
||||
generationType?: GenerationType
|
||||
collapsible?: boolean // Whether the code block can be collapsed
|
||||
defaultCollapsed?: boolean // Whether the code block is collapsed by default
|
||||
// OAuth specific properties
|
||||
provider?: string
|
||||
// OAuth specific properties - serviceId is the canonical identifier for OAuth services
|
||||
serviceId?: string
|
||||
requiredScopes?: string[]
|
||||
// File selector specific properties
|
||||
|
||||
@@ -138,6 +138,52 @@ const registry: Record<SelectorKey, SelectorDefinition> = {
|
||||
}))
|
||||
},
|
||||
},
|
||||
'microsoft.chats': {
|
||||
key: 'microsoft.chats',
|
||||
staleTime: SELECTOR_STALE,
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'microsoft.chats',
|
||||
context.credentialId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const body = JSON.stringify({ credential: context.credentialId })
|
||||
const data = await fetchJson<{ chats: { id: string; displayName: string }[] }>(
|
||||
'/api/tools/microsoft-teams/chats',
|
||||
{ method: 'POST', body }
|
||||
)
|
||||
return (data.chats || []).map((chat) => ({
|
||||
id: chat.id,
|
||||
label: chat.displayName,
|
||||
}))
|
||||
},
|
||||
},
|
||||
'microsoft.channels': {
|
||||
key: 'microsoft.channels',
|
||||
staleTime: SELECTOR_STALE,
|
||||
getQueryKey: ({ context }: SelectorQueryArgs) => [
|
||||
'selectors',
|
||||
'microsoft.channels',
|
||||
context.credentialId ?? 'none',
|
||||
context.teamId ?? 'none',
|
||||
],
|
||||
enabled: ({ context }) => Boolean(context.credentialId && context.teamId),
|
||||
fetchList: async ({ context }: SelectorQueryArgs) => {
|
||||
const body = JSON.stringify({
|
||||
credential: context.credentialId,
|
||||
teamId: context.teamId,
|
||||
})
|
||||
const data = await fetchJson<{ channels: { id: string; displayName: string }[] }>(
|
||||
'/api/tools/microsoft-teams/channels',
|
||||
{ method: 'POST', body }
|
||||
)
|
||||
return (data.channels || []).map((channel) => ({
|
||||
id: channel.id,
|
||||
label: channel.displayName,
|
||||
}))
|
||||
},
|
||||
},
|
||||
'wealthbox.contacts': {
|
||||
key: 'wealthbox.contacts',
|
||||
staleTime: SELECTOR_STALE,
|
||||
|
||||
@@ -64,9 +64,10 @@ function resolveFileSelector(
|
||||
mimeType: subBlock.mimeType,
|
||||
})
|
||||
|
||||
const provider = subBlock.provider || subBlock.serviceId || ''
|
||||
// Use serviceId as the canonical identifier
|
||||
const serviceId = subBlock.serviceId || ''
|
||||
|
||||
switch (provider) {
|
||||
switch (serviceId) {
|
||||
case 'google-calendar':
|
||||
return { key: 'google.calendar', context, allowSearch: false }
|
||||
case 'confluence':
|
||||
@@ -74,7 +75,15 @@ function resolveFileSelector(
|
||||
case 'jira':
|
||||
return { key: 'jira.issues', context, allowSearch: true }
|
||||
case 'microsoft-teams':
|
||||
return { key: 'microsoft.teams', context, allowSearch: true }
|
||||
// Route to the correct selector based on what type of resource is being selected
|
||||
if (subBlock.id === 'chatId') {
|
||||
return { key: 'microsoft.chats', context, allowSearch: false }
|
||||
}
|
||||
if (subBlock.id === 'channelId') {
|
||||
return { key: 'microsoft.channels', context, allowSearch: false }
|
||||
}
|
||||
// Default to teams selector for teamId
|
||||
return { key: 'microsoft.teams', context, allowSearch: false }
|
||||
case 'wealthbox':
|
||||
return { key: 'wealthbox.contacts', context, allowSearch: true }
|
||||
case 'microsoft-planner':
|
||||
@@ -89,36 +98,33 @@ function resolveFileSelector(
|
||||
return { key: 'google.drive', context, allowSearch: true }
|
||||
case 'google-docs':
|
||||
return { key: 'google.drive', context, allowSearch: true }
|
||||
case 'onedrive': {
|
||||
const key: SelectorKey = subBlock.mimeType === 'file' ? 'onedrive.files' : 'onedrive.folders'
|
||||
return { key, context, allowSearch: true }
|
||||
}
|
||||
case 'sharepoint':
|
||||
return { key: 'sharepoint.sites', context, allowSearch: true }
|
||||
default:
|
||||
break
|
||||
return { key: null, context, allowSearch: true }
|
||||
}
|
||||
|
||||
if (subBlock.serviceId === 'onedrive') {
|
||||
const key: SelectorKey = subBlock.mimeType === 'file' ? 'onedrive.files' : 'onedrive.folders'
|
||||
return { key, context, allowSearch: true }
|
||||
}
|
||||
|
||||
if (subBlock.serviceId === 'sharepoint') {
|
||||
return { key: 'sharepoint.sites', context, allowSearch: true }
|
||||
}
|
||||
|
||||
if (subBlock.serviceId === 'google-sheets') {
|
||||
return { key: 'google.drive', context, allowSearch: true }
|
||||
}
|
||||
|
||||
return { key: null, context, allowSearch: true }
|
||||
}
|
||||
|
||||
function resolveFolderSelector(
|
||||
subBlock: SubBlockConfig,
|
||||
args: SelectorResolutionArgs
|
||||
): SelectorResolution {
|
||||
const provider = (subBlock.provider || subBlock.serviceId || 'gmail').toLowerCase()
|
||||
const key: SelectorKey = provider === 'outlook' ? 'outlook.folders' : 'gmail.labels'
|
||||
return {
|
||||
key,
|
||||
context: buildBaseContext(args),
|
||||
allowSearch: true,
|
||||
const serviceId = subBlock.serviceId?.toLowerCase()
|
||||
if (!serviceId) {
|
||||
return { key: null, context: buildBaseContext(args), allowSearch: true }
|
||||
}
|
||||
|
||||
switch (serviceId) {
|
||||
case 'gmail':
|
||||
return { key: 'gmail.labels', context: buildBaseContext(args), allowSearch: true }
|
||||
case 'outlook':
|
||||
return { key: 'outlook.folders', context: buildBaseContext(args), allowSearch: true }
|
||||
default:
|
||||
return { key: null, context: buildBaseContext(args), allowSearch: true }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,8 +132,8 @@ function resolveChannelSelector(
|
||||
subBlock: SubBlockConfig,
|
||||
args: SelectorResolutionArgs
|
||||
): SelectorResolution {
|
||||
const provider = subBlock.provider || 'slack'
|
||||
if (provider !== 'slack') {
|
||||
const serviceId = subBlock.serviceId
|
||||
if (serviceId !== 'slack') {
|
||||
return { key: null, context: buildBaseContext(args), allowSearch: true }
|
||||
}
|
||||
return {
|
||||
@@ -141,22 +147,18 @@ function resolveProjectSelector(
|
||||
subBlock: SubBlockConfig,
|
||||
args: SelectorResolutionArgs
|
||||
): SelectorResolution {
|
||||
const provider = subBlock.provider || 'jira'
|
||||
const serviceId = subBlock.serviceId
|
||||
const context = buildBaseContext(args)
|
||||
|
||||
if (provider === 'linear') {
|
||||
const key: SelectorKey = subBlock.id === 'teamId' ? 'linear.teams' : 'linear.projects'
|
||||
return {
|
||||
key,
|
||||
context,
|
||||
allowSearch: true,
|
||||
switch (serviceId) {
|
||||
case 'linear': {
|
||||
const key: SelectorKey = subBlock.id === 'teamId' ? 'linear.teams' : 'linear.projects'
|
||||
return { key, context, allowSearch: true }
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
key: 'jira.projects',
|
||||
context,
|
||||
allowSearch: true,
|
||||
case 'jira':
|
||||
return { key: 'jira.projects', context, allowSearch: true }
|
||||
default:
|
||||
return { key: null, context, allowSearch: true }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ export type SelectorKey =
|
||||
| 'linear.teams'
|
||||
| 'confluence.pages'
|
||||
| 'microsoft.teams'
|
||||
| 'microsoft.chats'
|
||||
| 'microsoft.channels'
|
||||
| 'wealthbox.contacts'
|
||||
| 'onedrive.files'
|
||||
| 'onedrive.folders'
|
||||
@@ -33,7 +35,6 @@ export interface SelectorContext {
|
||||
workspaceId?: string
|
||||
workflowId?: string
|
||||
credentialId?: string
|
||||
provider?: string
|
||||
serviceId?: string
|
||||
domain?: string
|
||||
teamId?: string
|
||||
|
||||
@@ -39,7 +39,6 @@ export interface CopilotSubblockMetadata {
|
||||
language?: string
|
||||
generationType?: string
|
||||
// OAuth/credential properties
|
||||
provider?: string
|
||||
serviceId?: string
|
||||
requiredScopes?: string[]
|
||||
// File properties
|
||||
@@ -627,7 +626,6 @@ function processSubBlock(sb: any): CopilotSubblockMetadata {
|
||||
generationType: sb.generationType,
|
||||
|
||||
// OAuth/credential properties
|
||||
provider: sb.provider,
|
||||
serviceId: sb.serviceId,
|
||||
requiredScopes: sb.requiredScopes,
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ export enum CredentialType {
|
||||
// Type for credential requirement
|
||||
export interface CredentialRequirement {
|
||||
type: CredentialType
|
||||
provider?: string // For OAuth (e.g., 'google-drive', 'slack')
|
||||
serviceId?: string // For OAuth (e.g., 'google-drive', 'slack')
|
||||
label: string // Human-readable label
|
||||
blockType: string // The block type that requires this
|
||||
subBlockId: string // The subblock ID for reference
|
||||
@@ -72,7 +72,7 @@ export function extractRequiredCredentials(state: any): CredentialRequirement[]
|
||||
seen.add(key)
|
||||
credentials.push({
|
||||
type: CredentialType.OAUTH,
|
||||
provider: block.type,
|
||||
serviceId: block.type,
|
||||
label: `Credential for ${blockName}`,
|
||||
blockType: block.type,
|
||||
subBlockId: 'oauth',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { getProviderIdFromServiceId, getServiceIdFromScopes } from '@/lib/oauth/oauth'
|
||||
import { getProviderIdFromServiceId } from '@/lib/oauth/oauth'
|
||||
import { getBlock } from '@/blocks/index'
|
||||
import type { SubBlockConfig } from '@/blocks/types'
|
||||
import type { BlockState } from '@/stores/workflows/workflow/types'
|
||||
@@ -52,7 +52,7 @@ export async function resolveCredentialsForWorkflow(
|
||||
|
||||
logger.debug(`Checking credential for ${blockId}.${subBlockId}`, {
|
||||
blockType: blockState.type,
|
||||
provider: subBlockConfig.provider,
|
||||
serviceId: subBlockConfig.serviceId,
|
||||
hasExistingValue: !!existingValue,
|
||||
existingValue,
|
||||
})
|
||||
@@ -70,13 +70,13 @@ export async function resolveCredentialsForWorkflow(
|
||||
resolvedValues[blockId][subBlockId] = credentialId
|
||||
logger.info(`Auto-selected credential for ${blockId}.${subBlockId}`, {
|
||||
blockType: blockState.type,
|
||||
provider: subBlockConfig.provider,
|
||||
serviceId: subBlockConfig.serviceId,
|
||||
credentialId,
|
||||
})
|
||||
} else {
|
||||
logger.info(`No credential auto-selected for ${blockId}.${subBlockId}`, {
|
||||
blockType: blockState.type,
|
||||
provider: subBlockConfig.provider,
|
||||
serviceId: subBlockConfig.serviceId,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -102,7 +102,6 @@ export async function resolveCredentialsForWorkflow(
|
||||
*/
|
||||
async function resolveCredentialForSubBlock(
|
||||
subBlockConfig: SubBlockConfig & {
|
||||
provider?: string
|
||||
requiredScopes?: string[]
|
||||
serviceId?: string
|
||||
},
|
||||
@@ -110,29 +109,26 @@ async function resolveCredentialForSubBlock(
|
||||
userId?: string
|
||||
): Promise<string | null> {
|
||||
try {
|
||||
const provider = subBlockConfig.provider
|
||||
const requiredScopes = subBlockConfig.requiredScopes || []
|
||||
const serviceId = subBlockConfig.serviceId
|
||||
|
||||
logger.debug('Resolving credential for subblock', {
|
||||
blockType: blockState.type,
|
||||
provider,
|
||||
serviceId,
|
||||
requiredScopes,
|
||||
userId,
|
||||
})
|
||||
|
||||
if (!provider) {
|
||||
logger.debug('No provider specified, skipping credential resolution')
|
||||
if (!serviceId) {
|
||||
logger.debug('No serviceId specified, skipping credential resolution')
|
||||
return null
|
||||
}
|
||||
|
||||
// Derive service and provider IDs
|
||||
const effectiveServiceId = serviceId || getServiceIdFromScopes(provider as any, requiredScopes)
|
||||
const effectiveProviderId = getProviderIdFromServiceId(effectiveServiceId)
|
||||
// Derive providerId from serviceId using OAuth config
|
||||
const effectiveProviderId = getProviderIdFromServiceId(serviceId)
|
||||
|
||||
logger.debug('Derived provider info', {
|
||||
effectiveServiceId,
|
||||
serviceId,
|
||||
effectiveProviderId,
|
||||
})
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ export interface UIComponentConfig {
|
||||
condition?: ComponentCondition
|
||||
title?: string
|
||||
value?: unknown
|
||||
provider?: string
|
||||
serviceId?: string
|
||||
requiredScopes?: string[]
|
||||
mimeType?: string
|
||||
@@ -50,7 +49,6 @@ export interface SubBlockConfig {
|
||||
password?: boolean
|
||||
condition?: ComponentCondition
|
||||
value?: unknown
|
||||
provider?: string
|
||||
serviceId?: string
|
||||
requiredScopes?: string[]
|
||||
mimeType?: string
|
||||
@@ -277,7 +275,6 @@ export function getToolParametersConfig(
|
||||
condition: subBlock.condition,
|
||||
title: subBlock.title,
|
||||
value: subBlock.value,
|
||||
provider: subBlock.provider,
|
||||
serviceId: subBlock.serviceId,
|
||||
requiredScopes: subBlock.requiredScopes,
|
||||
mimeType: subBlock.mimeType,
|
||||
|
||||
@@ -16,7 +16,7 @@ export const airtableWebhookTrigger: TriggerConfig = {
|
||||
title: 'Credentials',
|
||||
type: 'oauth-input',
|
||||
description: 'This trigger requires airtable credentials to access your account.',
|
||||
provider: 'airtable',
|
||||
serviceId: 'airtable',
|
||||
requiredScopes: [],
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
|
||||
@@ -19,7 +19,7 @@ export const gmailPollingTrigger: TriggerConfig = {
|
||||
title: 'Credentials',
|
||||
type: 'oauth-input',
|
||||
description: 'This trigger requires google email credentials to access your account.',
|
||||
provider: 'google-email',
|
||||
serviceId: 'gmail',
|
||||
requiredScopes: [],
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
|
||||
@@ -22,7 +22,6 @@ export const jiraWebhookSubBlocks: SubBlockConfig[] = [
|
||||
id: 'triggerCredentials',
|
||||
title: 'Jira Credentials',
|
||||
type: 'oauth-input',
|
||||
provider: 'jira',
|
||||
serviceId: 'jira',
|
||||
requiredScopes: [
|
||||
'read:jira-work',
|
||||
|
||||
@@ -16,7 +16,7 @@ export const microsoftTeamsChatSubscriptionTrigger: TriggerConfig = {
|
||||
title: 'Credentials',
|
||||
type: 'oauth-input',
|
||||
description: 'This trigger requires microsoft teams credentials to access your account.',
|
||||
provider: 'microsoft-teams',
|
||||
serviceId: 'microsoft-teams',
|
||||
requiredScopes: [
|
||||
'openid',
|
||||
'profile',
|
||||
|
||||
@@ -19,7 +19,7 @@ export const outlookPollingTrigger: TriggerConfig = {
|
||||
title: 'Credentials',
|
||||
type: 'oauth-input',
|
||||
description: 'This trigger requires outlook credentials to access your account.',
|
||||
provider: 'outlook',
|
||||
serviceId: 'outlook',
|
||||
requiredScopes: [],
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
|
||||
@@ -16,7 +16,7 @@ export const webflowCollectionItemChangedTrigger: TriggerConfig = {
|
||||
title: 'Credentials',
|
||||
type: 'oauth-input',
|
||||
description: 'This trigger requires webflow credentials to access your account.',
|
||||
provider: 'webflow',
|
||||
serviceId: 'webflow',
|
||||
requiredScopes: [],
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
|
||||
@@ -29,7 +29,7 @@ export const webflowCollectionItemCreatedTrigger: TriggerConfig = {
|
||||
title: 'Credentials',
|
||||
type: 'oauth-input',
|
||||
description: 'This trigger requires webflow credentials to access your account.',
|
||||
provider: 'webflow',
|
||||
serviceId: 'webflow',
|
||||
requiredScopes: [],
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
|
||||
@@ -16,7 +16,7 @@ export const webflowCollectionItemDeletedTrigger: TriggerConfig = {
|
||||
title: 'Credentials',
|
||||
type: 'oauth-input',
|
||||
description: 'This trigger requires webflow credentials to access your account.',
|
||||
provider: 'webflow',
|
||||
serviceId: 'webflow',
|
||||
requiredScopes: [],
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
|
||||
@@ -16,7 +16,7 @@ export const webflowFormSubmissionTrigger: TriggerConfig = {
|
||||
title: 'Credentials',
|
||||
type: 'oauth-input',
|
||||
description: 'This trigger requires webflow credentials to access your account.',
|
||||
provider: 'webflow',
|
||||
serviceId: 'webflow',
|
||||
requiredScopes: [],
|
||||
required: true,
|
||||
mode: 'trigger',
|
||||
|
||||
Reference in New Issue
Block a user