Skip to content

Commit adfac54

Browse files
committed
Merge branch 'fix/tabl' of github.com:simstudioai/sim into fix/tabl
2 parents 41799e7 + 8ca72d6 commit adfac54

7 files changed

Lines changed: 267 additions & 19 deletions

File tree

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,6 @@ function ColumnConfigBody({
135135
toast.success(`Saved "${trimmedName}"`)
136136
onClose()
137137
} catch (err) {
138-
// Server validation errors carry a Zod issue array on the body; surface
139-
// them inline next to the offending field instead of as a raw toast.
140138
if (isValidationError(err)) {
141139
const nameIssue =
142140
findValidationIssue(err, ['updates', 'name']) ??
@@ -146,8 +144,8 @@ function ColumnConfigBody({
146144
setNameError(nameIssue.message)
147145
return
148146
}
147+
toast.error(toError(err).message)
149148
}
150-
toast.error(toError(err).message)
151149
}
152150
}
153151

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const WORKFLOW_SEARCH_HIGHLIGHT_CLASS =
2+
'rounded-sm bg-orange-400 shadow-[3px_0_0_#fb923c,-3px_0_0_#fb923c]'

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/sub-block.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/c
5555
import type { SubBlockConfig } from '@/blocks/types'
5656
import { useWebhookManagement } from '@/hooks/use-webhook-management'
5757
import type { ActiveSearchTarget } from '@/stores/panel/editor/store'
58+
import { WORKFLOW_SEARCH_HIGHLIGHT_CLASS } from '../constants'
5859

5960
const SLACK_OVERRIDES: SelectorOverrides = {
6061
transformContext: (context, deps) => {
@@ -73,9 +74,6 @@ const FOLDER_OVERRIDES: SelectorOverrides = {
7374
},
7475
}
7576

76-
const WORKFLOW_SEARCH_HIGHLIGHT_CLASS =
77-
'rounded-sm bg-orange-400 shadow-[3px_0_0_#fb923c,-3px_0_0_#fb923c]'
78-
7977
function hasNestedWorkflowSearchHighlight(
8078
config: SubBlockConfig,
8179
activeSearchTarget?: ActiveSearchTarget | null

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/subflow-editor/subflow-editor.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ import type { BlockState } from '@/stores/workflows/workflow/types'
1010
import type { ConnectedBlock } from '../../hooks/use-block-connections'
1111
import { useSubflowEditor } from '../../hooks/use-subflow-editor'
1212
import { ConnectionBlocks } from '../connection-blocks'
13-
14-
const WORKFLOW_SEARCH_HIGHLIGHT_CLASS =
15-
'rounded-sm bg-orange-400 shadow-[3px_0_0_#fb923c,-3px_0_0_#fb923c]'
13+
import { WORKFLOW_SEARCH_HIGHLIGHT_CLASS } from '../constants'
1614

1715
interface SubflowEditorProps {
1816
currentBlock: BlockState

apps/sim/hooks/queries/tables.ts

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,10 @@ export function useCreateTable(workspaceId: string) {
466466
body: { ...params, workspaceId },
467467
})
468468
},
469+
onError: (error) => {
470+
if (isValidationError(error)) return
471+
toast.error(error.message, { duration: 5000 })
472+
},
469473
onSettled: () => {
470474
queryClient.invalidateQueries({ queryKey: tableKeys.lists() })
471475
},
@@ -485,6 +489,10 @@ export function useAddTableColumn({ workspaceId, tableId }: RowMutationContext)
485489
body: { workspaceId, column },
486490
})
487491
},
492+
onError: (error) => {
493+
if (isValidationError(error)) return
494+
toast.error(error.message, { duration: 5000 })
495+
},
488496
onSettled: () => {
489497
invalidateTableSchema(queryClient, tableId)
490498
},
@@ -528,6 +536,10 @@ export function useDeleteTable(workspaceId: string) {
528536
query: { workspaceId },
529537
})
530538
},
539+
onError: (error) => {
540+
if (isValidationError(error)) return
541+
toast.error(error.message, { duration: 5000 })
542+
},
531543
onSettled: (_data, _error, tableId) => {
532544
queryClient.invalidateQueries({ queryKey: tableKeys.lists() })
533545
queryClient.removeQueries({ queryKey: tableKeys.detail(tableId) })
@@ -814,6 +826,10 @@ export function useDeleteTableRow({ workspaceId, tableId }: RowMutationContext)
814826
body: { workspaceId },
815827
})
816828
},
829+
onError: (error) => {
830+
if (isValidationError(error)) return
831+
toast.error(error.message, { duration: 5000 })
832+
},
817833
onSettled: () => {
818834
invalidateRowCount(queryClient, tableId)
819835
},
@@ -851,6 +867,10 @@ export function useDeleteTableRows({ workspaceId, tableId }: RowMutationContext)
851867

852868
return { deletedRowIds }
853869
},
870+
onError: (error) => {
871+
if (isValidationError(error)) return
872+
toast.error(error.message, { duration: 5000 })
873+
},
854874
onSettled: () => {
855875
invalidateRowCount(queryClient, tableId)
856876
},
@@ -1028,6 +1048,10 @@ export function useRestoreTable() {
10281048
params: { tableId },
10291049
})
10301050
},
1051+
onError: (error) => {
1052+
if (isValidationError(error)) return
1053+
toast.error(error.message, { duration: 5000 })
1054+
},
10311055
onSettled: () => {
10321056
queryClient.invalidateQueries({ queryKey: tableKeys.lists() })
10331057
},
@@ -1064,11 +1088,12 @@ export function useUploadCsvToTable() {
10641088

10651089
return response.json()
10661090
},
1067-
onSettled: () => {
1068-
queryClient.invalidateQueries({ queryKey: tableKeys.lists() })
1069-
},
10701091
onError: (error) => {
10711092
logger.error('Failed to upload CSV:', error)
1093+
toast.error(error.message, { duration: 5000 })
1094+
},
1095+
onSettled: () => {
1096+
queryClient.invalidateQueries({ queryKey: tableKeys.lists() })
10721097
},
10731098
})
10741099
}
@@ -1140,13 +1165,14 @@ export function useImportCsvIntoTable() {
11401165

11411166
return response.json()
11421167
},
1168+
onError: (error) => {
1169+
logger.error('Failed to import CSV into table:', error)
1170+
toast.error(error.message, { duration: 5000 })
1171+
},
11431172
onSettled: (_data, _error, variables) => {
11441173
if (!variables) return
11451174
invalidateRowCount(queryClient, variables.tableId)
11461175
},
1147-
onError: (error) => {
1148-
logger.error('Failed to import CSV into table:', error)
1149-
},
11501176
})
11511177
}
11521178

@@ -1438,6 +1464,10 @@ export function useAddWorkflowGroup({ workspaceId, tableId }: RowMutationContext
14381464
body: { workspaceId, group, outputColumns },
14391465
})
14401466
},
1467+
onError: (error) => {
1468+
if (isValidationError(error)) return
1469+
toast.error(error.message, { duration: 5000 })
1470+
},
14411471
onSettled: () => {
14421472
invalidateTableSchema(queryClient, tableId)
14431473
},
@@ -1464,6 +1494,10 @@ export function useUpdateWorkflowGroup({ workspaceId, tableId }: RowMutationCont
14641494
body: { workspaceId, ...vars },
14651495
})
14661496
},
1497+
onError: (error) => {
1498+
if (isValidationError(error)) return
1499+
toast.error(error.message, { duration: 5000 })
1500+
},
14671501
onSettled: () => {
14681502
invalidateTableSchema(queryClient, tableId)
14691503
queryClient.invalidateQueries({ queryKey: tableKeys.rowsRoot(tableId) })
@@ -1484,6 +1518,10 @@ export function useDeleteWorkflowGroup({ workspaceId, tableId }: RowMutationCont
14841518
body: { workspaceId, groupId },
14851519
})
14861520
},
1521+
onError: (error) => {
1522+
if (isValidationError(error)) return
1523+
toast.error(error.message, { duration: 5000 })
1524+
},
14871525
onSettled: () => {
14881526
invalidateTableSchema(queryClient, tableId)
14891527
queryClient.invalidateQueries({ queryKey: tableKeys.rowsRoot(tableId) })

apps/sim/lib/workflows/search-replace/indexer.test.ts

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,160 @@ describe('indexWorkflowSearchMatches', () => {
457457
])
458458
})
459459

460+
it('does not index skill-shaped values even when persisted under a legacy id', () => {
461+
const workflow = createSearchReplaceWorkflowFixture()
462+
workflow.blocks['legacy-agent-1'] = {
463+
id: 'legacy-agent-1',
464+
type: 'custom',
465+
name: 'Legacy Agent',
466+
position: { x: 0, y: 0 },
467+
enabled: true,
468+
outputs: {},
469+
subBlocks: {
470+
legacySkills: {
471+
id: 'legacySkills',
472+
type: 'short-input',
473+
value: [{ skillId: 'skill-vik-id', name: 'vik-skill' }],
474+
},
475+
},
476+
}
477+
478+
const matches = indexWorkflowSearchMatches({
479+
workflow,
480+
query: 'vik',
481+
mode: 'text',
482+
blockConfigs: {
483+
...SEARCH_REPLACE_BLOCK_CONFIGS,
484+
custom: { subBlocks: [] },
485+
},
486+
}).filter((match) => match.blockId === 'legacy-agent-1')
487+
488+
expect(matches).toEqual([])
489+
})
490+
491+
it('does not index upload or dynamic control internals as text', () => {
492+
const workflow = createSearchReplaceWorkflowFixture()
493+
workflow.blocks['dynamic-1'] = {
494+
id: 'dynamic-1',
495+
type: 'custom',
496+
name: 'Dynamic Block',
497+
position: { x: 0, y: 0 },
498+
enabled: true,
499+
outputs: {},
500+
subBlocks: {
501+
upload: {
502+
id: 'upload',
503+
type: 'file-upload',
504+
value: {
505+
name: 'customer.csv',
506+
path: '/workspace/customer.csv',
507+
key: 'storage-customer-key',
508+
},
509+
},
510+
mcpArgs: {
511+
id: 'mcpArgs',
512+
type: 'mcp-dynamic-args',
513+
value: { prompt: 'customer prompt' },
514+
},
515+
slider: {
516+
id: 'slider',
517+
type: 'slider',
518+
value: 42,
519+
},
520+
enabled: {
521+
id: 'enabled',
522+
type: 'switch',
523+
value: true,
524+
},
525+
},
526+
}
527+
528+
const matches = indexWorkflowSearchMatches({
529+
workflow,
530+
query: 'customer',
531+
mode: 'text',
532+
blockConfigs: {
533+
...SEARCH_REPLACE_BLOCK_CONFIGS,
534+
custom: {
535+
subBlocks: [
536+
{ id: 'upload', title: 'Upload', type: 'file-upload' },
537+
{ id: 'mcpArgs', title: 'MCP Args', type: 'mcp-dynamic-args' },
538+
{ id: 'slider', title: 'Slider', type: 'slider' },
539+
{ id: 'enabled', title: 'Enabled', type: 'switch' },
540+
],
541+
},
542+
},
543+
}).filter((match) => match.blockId === 'dynamic-1')
544+
545+
expect(matches).toEqual([])
546+
})
547+
548+
it('indexes only safe user-facing paths inside tool input values', () => {
549+
const workflow = createSearchReplaceWorkflowFixture()
550+
workflow.blocks['tool-input-1'] = {
551+
id: 'tool-input-1',
552+
type: 'custom',
553+
name: 'Tool Input Block',
554+
position: { x: 0, y: 0 },
555+
enabled: true,
556+
outputs: {},
557+
subBlocks: {
558+
tools: {
559+
id: 'tools',
560+
type: 'tool-input',
561+
value: [
562+
{
563+
type: 'native',
564+
toolId: 'gmail_customer_tool',
565+
operation: 'send_customer_message',
566+
title: 'Customer notifier',
567+
params: {
568+
body: 'hello customer',
569+
credentialId: 'credential-customer-id',
570+
inputMapping: JSON.stringify({ query: 'customer json value' }),
571+
},
572+
schema: {
573+
description: 'customer schema text',
574+
},
575+
},
576+
],
577+
},
578+
},
579+
}
580+
581+
const matches = indexWorkflowSearchMatches({
582+
workflow,
583+
query: 'customer',
584+
mode: 'text',
585+
blockConfigs: {
586+
...SEARCH_REPLACE_BLOCK_CONFIGS,
587+
custom: {
588+
subBlocks: [{ id: 'tools', title: 'Tools', type: 'tool-input' }],
589+
},
590+
},
591+
}).filter((match) => match.blockId === 'tool-input-1')
592+
593+
expect(matches).toEqual(
594+
expect.arrayContaining([
595+
expect.objectContaining({
596+
subBlockId: 'tools',
597+
valuePath: [0, 'title'],
598+
searchText: 'Customer notifier',
599+
}),
600+
expect.objectContaining({
601+
subBlockId: 'tools',
602+
valuePath: [0, 'params', 'body'],
603+
searchText: 'hello customer',
604+
}),
605+
])
606+
)
607+
expect(matches.some((match) => match.valuePath.includes('toolId'))).toBe(false)
608+
expect(matches.some((match) => match.valuePath.includes('operation'))).toBe(false)
609+
expect(matches.some((match) => match.valuePath.includes('credentialId'))).toBe(false)
610+
expect(matches.some((match) => match.valuePath.includes('inputMapping'))).toBe(false)
611+
expect(matches.some((match) => match.valuePath.includes('schema'))).toBe(false)
612+
})
613+
460614
it('does not index condition-hidden subblocks', () => {
461615
const workflow = createSearchReplaceWorkflowFixture()
462616
workflow.blocks['condition-1'] = {

0 commit comments

Comments
 (0)