mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
Add service account credential block prompting for google service account
This commit is contained in:
@@ -1,77 +1,18 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import type { CanonicalModeOverrides } from '@/lib/workflows/subblocks/visibility'
|
||||
import {
|
||||
buildCanonicalIndex,
|
||||
evaluateSubBlockCondition,
|
||||
isSubBlockFeatureEnabled,
|
||||
isSubBlockHidden,
|
||||
isSubBlockVisibleForMode,
|
||||
resolveDependencyValue,
|
||||
} from '@/lib/workflows/subblocks/visibility'
|
||||
import type { BlockConfig, SubBlockConfig, SubBlockType } from '@/blocks/types'
|
||||
import { useWorkspaceCredential } from '@/hooks/queries/credentials'
|
||||
import { usePermissionConfig } from '@/hooks/use-permission-config'
|
||||
import { useReactiveConditions } from '@/hooks/use-reactive-conditions'
|
||||
import { useWorkflowDiffStore } from '@/stores/workflow-diff'
|
||||
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
||||
import { mergeSubblockState } from '@/stores/workflows/utils'
|
||||
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
|
||||
|
||||
/**
|
||||
* Evaluates reactive conditions for subblocks. Always calls the same hooks
|
||||
* regardless of whether a reactive condition exists (Rules of Hooks).
|
||||
*
|
||||
* Returns a Set of subblock IDs that should be hidden.
|
||||
*/
|
||||
function useReactiveConditions(
|
||||
subBlocks: SubBlockConfig[],
|
||||
blockId: string,
|
||||
activeWorkflowId: string | null,
|
||||
canonicalModeOverrides?: CanonicalModeOverrides
|
||||
): Set<string> {
|
||||
const reactiveSubBlock = useMemo(() => subBlocks.find((sb) => sb.reactiveCondition), [subBlocks])
|
||||
const reactiveCond = reactiveSubBlock?.reactiveCondition
|
||||
|
||||
const canonicalIndex = useMemo(() => buildCanonicalIndex(subBlocks), [subBlocks])
|
||||
|
||||
// Resolve watchFields through canonical index to get the active credential value
|
||||
const watchedCredentialId = useSubBlockStore(
|
||||
useCallback(
|
||||
(state) => {
|
||||
if (!reactiveCond || !activeWorkflowId) return ''
|
||||
const blockValues = state.workflowValues[activeWorkflowId]?.[blockId] ?? {}
|
||||
for (const field of reactiveCond.watchFields) {
|
||||
const val = resolveDependencyValue(
|
||||
field,
|
||||
blockValues,
|
||||
canonicalIndex,
|
||||
canonicalModeOverrides
|
||||
)
|
||||
if (val && typeof val === 'string') return val
|
||||
}
|
||||
return ''
|
||||
},
|
||||
[reactiveCond, activeWorkflowId, blockId, canonicalIndex, canonicalModeOverrides]
|
||||
)
|
||||
)
|
||||
|
||||
// Always call useWorkspaceCredential (stable hook count), disable when not needed
|
||||
const { data: credential } = useWorkspaceCredential(
|
||||
watchedCredentialId || undefined,
|
||||
Boolean(reactiveCond && watchedCredentialId)
|
||||
)
|
||||
|
||||
return useMemo(() => {
|
||||
const hidden = new Set<string>()
|
||||
if (!reactiveSubBlock || !reactiveCond) return hidden
|
||||
|
||||
const conditionMet = credential?.type === reactiveCond.requiredType
|
||||
if (!conditionMet) {
|
||||
hidden.add(reactiveSubBlock.id)
|
||||
}
|
||||
return hidden
|
||||
}, [reactiveSubBlock, reactiveCond, credential?.type])
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom hook for computing subblock layout in the editor panel.
|
||||
* Determines which subblocks should be visible based on mode, conditions, and feature flags.
|
||||
|
||||
@@ -47,6 +47,7 @@ import { useReactivateSchedule, useScheduleInfo } from '@/hooks/queries/schedule
|
||||
import { useSkills } from '@/hooks/queries/skills'
|
||||
import { useTablesList } from '@/hooks/queries/tables'
|
||||
import { useWorkflowMap } from '@/hooks/queries/workflows'
|
||||
import { useReactiveConditions } from '@/hooks/use-reactive-conditions'
|
||||
import { useSelectorDisplayName } from '@/hooks/use-selector-display-name'
|
||||
import { useVariablesStore } from '@/stores/panel'
|
||||
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
||||
@@ -942,6 +943,13 @@ export const WorkflowBlock = memo(function WorkflowBlock({
|
||||
const canonicalIndex = useMemo(() => buildCanonicalIndex(config.subBlocks), [config.subBlocks])
|
||||
const canonicalModeOverrides = currentStoreBlock?.data?.canonicalModes
|
||||
|
||||
const hiddenByReactiveCondition = useReactiveConditions(
|
||||
config.subBlocks,
|
||||
id,
|
||||
activeWorkflowId,
|
||||
canonicalModeOverrides
|
||||
)
|
||||
|
||||
const subBlockRowsData = useMemo(() => {
|
||||
const rows: SubBlockConfig[][] = []
|
||||
let currentRow: SubBlockConfig[] = []
|
||||
@@ -979,6 +987,7 @@ export const WorkflowBlock = memo(function WorkflowBlock({
|
||||
const visibleSubBlocks = config.subBlocks.filter((block) => {
|
||||
if (block.hidden) return false
|
||||
if (block.hideFromPreview) return false
|
||||
if (hiddenByReactiveCondition.has(block.id)) return false
|
||||
if (!isSubBlockFeatureEnabled(block)) return false
|
||||
if (isSubBlockHidden(block)) return false
|
||||
|
||||
@@ -1047,6 +1056,7 @@ export const WorkflowBlock = memo(function WorkflowBlock({
|
||||
canonicalModeOverrides,
|
||||
userPermissions.canEdit,
|
||||
canonicalIndex,
|
||||
hiddenByReactiveCondition,
|
||||
blockSubBlockValues,
|
||||
activeWorkflowId,
|
||||
])
|
||||
|
||||
62
apps/sim/hooks/use-reactive-conditions.ts
Normal file
62
apps/sim/hooks/use-reactive-conditions.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import type { CanonicalModeOverrides } from '@/lib/workflows/subblocks/visibility'
|
||||
import { buildCanonicalIndex, resolveDependencyValue } from '@/lib/workflows/subblocks/visibility'
|
||||
import type { SubBlockConfig } from '@/blocks/types'
|
||||
import { useWorkspaceCredential } from '@/hooks/queries/credentials'
|
||||
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
|
||||
|
||||
/**
|
||||
* Evaluates reactive conditions for subblocks. Always calls the same hooks
|
||||
* regardless of whether a reactive condition exists (Rules of Hooks).
|
||||
*
|
||||
* Returns a Set of subblock IDs that should be hidden.
|
||||
*/
|
||||
export function useReactiveConditions(
|
||||
subBlocks: SubBlockConfig[],
|
||||
blockId: string,
|
||||
activeWorkflowId: string | null,
|
||||
canonicalModeOverrides?: CanonicalModeOverrides
|
||||
): Set<string> {
|
||||
const reactiveSubBlock = useMemo(() => subBlocks.find((sb) => sb.reactiveCondition), [subBlocks])
|
||||
const reactiveCond = reactiveSubBlock?.reactiveCondition
|
||||
|
||||
const canonicalIndex = useMemo(() => buildCanonicalIndex(subBlocks), [subBlocks])
|
||||
|
||||
// Resolve watchFields through canonical index to get the active credential value
|
||||
const watchedCredentialId = useSubBlockStore(
|
||||
useCallback(
|
||||
(state) => {
|
||||
if (!reactiveCond || !activeWorkflowId) return ''
|
||||
const blockValues = state.workflowValues[activeWorkflowId]?.[blockId] ?? {}
|
||||
for (const field of reactiveCond.watchFields) {
|
||||
const val = resolveDependencyValue(
|
||||
field,
|
||||
blockValues,
|
||||
canonicalIndex,
|
||||
canonicalModeOverrides
|
||||
)
|
||||
if (val && typeof val === 'string') return val
|
||||
}
|
||||
return ''
|
||||
},
|
||||
[reactiveCond, activeWorkflowId, blockId, canonicalIndex, canonicalModeOverrides]
|
||||
)
|
||||
)
|
||||
|
||||
// Always call useWorkspaceCredential (stable hook count), disable when not needed
|
||||
const { data: credential } = useWorkspaceCredential(
|
||||
watchedCredentialId || undefined,
|
||||
Boolean(reactiveCond && watchedCredentialId)
|
||||
)
|
||||
|
||||
return useMemo(() => {
|
||||
const hidden = new Set<string>()
|
||||
if (!reactiveSubBlock || !reactiveCond) return hidden
|
||||
|
||||
const conditionMet = credential?.type === reactiveCond.requiredType
|
||||
if (!conditionMet) {
|
||||
hidden.add(reactiveSubBlock.id)
|
||||
}
|
||||
return hidden
|
||||
}, [reactiveSubBlock, reactiveCond, credential?.type])
|
||||
}
|
||||
@@ -5,12 +5,12 @@ import { getCopilotToolDescription } from '@/lib/copilot/tool-descriptions'
|
||||
import type { BaseServerTool } from '@/lib/copilot/tools/server/base-tool'
|
||||
import { GetBlocksMetadataInput, GetBlocksMetadataResult } from '@/lib/copilot/tools/shared/schemas'
|
||||
import { getAllowedIntegrationsFromEnv, isHosted } from '@/lib/core/config/feature-flags'
|
||||
import { getServiceAccountProviderForProviderId } from '@/lib/oauth/utils'
|
||||
import { registry as blockRegistry } from '@/blocks/registry'
|
||||
import { AuthMode, type BlockConfig, isHiddenFromDisplay } from '@/blocks/types'
|
||||
import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check'
|
||||
import { PROVIDER_DEFINITIONS } from '@/providers/models'
|
||||
import { tools as toolsRegistry } from '@/tools/registry'
|
||||
import { getServiceAccountProviderForProviderId } from '@/lib/oauth/utils'
|
||||
import { getTrigger, isTriggerValid } from '@/triggers'
|
||||
import { SYSTEM_SUBBLOCK_IDS } from '@/triggers/constants'
|
||||
|
||||
@@ -354,8 +354,7 @@ function transformBlockMetadata(metadata: CopilotBlockMetadata): any {
|
||||
const serviceAccountProviderId = getServiceAccountProviderForProviderId(serviceId)
|
||||
if (serviceAccountProviderId) {
|
||||
transformed.requiredCredentials.serviceAccountType = serviceAccountProviderId
|
||||
transformed.requiredCredentials.description =
|
||||
`OAuth or service account authentication supported for ${metadata.name}`
|
||||
transformed.requiredCredentials.description = `OAuth or service account authentication supported for ${metadata.name}`
|
||||
}
|
||||
} else if (metadata.authType === 'API Key') {
|
||||
transformed.requiredCredentials = {
|
||||
|
||||
Reference in New Issue
Block a user