Skip to content

Commit e4d8673

Browse files
authored
fix(integrations): address release review findings on the integration sweep (#10039)
* fix(integrations): validate Asana limits, Intercom snooze times, and Mailchimp template ids, clamp Airtable page size, accept Mailchimp batch arrays, and keep the Mailchimp email type option on update * fix(integrations): resolve the tool-call operation in the QuickBooks, SAP Concur, Attio, Intercom, Notion, Box, and HubSpot mappers * refactor(integrations): share the Intercom params mapper, reuse the boolean and JSON input helpers, and drop duplicated test helpers * fix(mailchimp): leave the email type option unchanged when its stored value is null * fix(integrations): seed the Mailchimp email type option per operation and tighten presence checks for Intercom, Airtable, Dropbox, and QuickBooks inputs * fix(integrations): seed the Mailchimp email type option from the operation's first option and validate the Intercom ticket snooze on tool calls * fix(mailchimp): seed the email type option per operation, keep Keep current outside create, and default an unset create value to true * fix(integrations): lead the Intercom role and Pipedrive deal status with No change in agent tool rows * fix(integrations): parse the Mailchimp update email type option like create, drop its block-level required, and keep a zero Asana limit for validation * fix(asana): treat a whitespace-only limit field as blank on tool calls
1 parent 6d73cad commit e4d8673

33 files changed

Lines changed: 631 additions & 367 deletions

‎apps/docs/content/docs/integrations/mailchimp.mdx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Create a new audience (list) in Mailchimp
8787
| `contact` | string | Yes | JSON object of contact information \(e.g., \{"company": "Acme", "address1": "123 Main St", "city": "NYC", "state": "NY", "zip": "10001", "country": "US"\}\) |
8888
| `permissionReminder` | string | Yes | Permission reminder text shown to subscribers \(e.g., "You signed up for updates on our website"\) |
8989
| `campaignDefaults` | string | Yes | JSON object of default campaign settings \(e.g., \{"from_name": "Acme", "from_email": "news@acme.com", "subject": "", "language": "en"\}\) |
90-
| `emailTypeOption` | string | Yes | Support multiple email formats: "true" or "false" |
90+
| `emailTypeOption` | string | No | Support multiple email formats: "true" or "false" \(default "true"\) |
9191

9292
#### Output
9393

@@ -115,7 +115,7 @@ Update an existing audience (list) in Mailchimp
115115
| `audienceName` | string | No | The name of the audience/list \(e.g., "Newsletter Subscribers"\) |
116116
| `permissionReminder` | string | No | Permission reminder text shown to subscribers \(e.g., "You signed up for updates on our website"\) |
117117
| `campaignDefaults` | string | No | JSON object of default campaign settings \(e.g., \{"from_name": "Acme", "from_email": "news@acme.com"\}\) |
118-
| `emailTypeOption` | string | No | Support multiple email formats: "true" or "false". Omit to leave it unchanged |
118+
| `emailTypeOption` | string | No | Whether the audience supports multiple email formats \("true" or "false"\). Leave unset to keep the current setting |
119119

120120
#### Output
121121

@@ -1789,7 +1789,7 @@ Create a new batch operation in Mailchimp
17891789

17901790
| Parameter | Type | Required | Description |
17911791
| --------- | ---- | -------- | ----------- |
1792-
| `operations` | string | Yes | JSON array of batch operations \(e.g., \[\{"method": "POST", "path": "/lists/\{list_id\}/members", "body": \{"email_address": "user@example.com", "status": "subscribed"\}\}\]\); an object body is sent as the JSON string Mailchimp expects |
1792+
| `operations` | json | Yes | JSON array of batch operations \(e.g., \[\{"method": "POST", "path": "/lists/\{list_id\}/members", "body": \{"email_address": "user@example.com", "status": "subscribed"\}\}\]\); an object body is sent as the JSON string Mailchimp expects |
17931793

17941794
#### Output
17951795

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1190,7 +1190,7 @@ function SubBlockComponent({
11901190
subBlockId={config.id}
11911191
content={
11921192
typeof config.value === 'function'
1193-
? config.value(subBlockValues || {})
1193+
? (config.value(subBlockValues || {}) ?? '')
11941194
: (config.defaultValue as string) || ''
11951195
}
11961196
/>

‎apps/sim/blocks/blocks/asana.test.ts‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,20 @@ describe('AsanaBlock agent tool calls with a configured field', () => {
9090
expect(mapped.workspace).toBe('ws-1')
9191
})
9292
})
93+
94+
describe('AsanaBlock canvas limits', () => {
95+
it('passes a numeric 0 limit through so validation can reject it', () => {
96+
expect(
97+
buildParams({ operation: 'get_task', workspace: 'ws-1', assignee: 'me', getTasks_limit: 0 })
98+
).toMatchObject({ limit: 0 })
99+
})
100+
101+
it('ignores a whitespace-only limit field so the model limit applies on a tool call', () => {
102+
expect(
103+
buildParams(
104+
{ workspace: 'ws-1', assignee: 'me', getTasks_limit: ' ', limit: 25 },
105+
{ operation: 'get_task' }
106+
)
107+
).toMatchObject({ limit: 25 })
108+
})
109+
})

‎apps/sim/blocks/blocks/asana.ts‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,14 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
532532
.filter((gid) => gid.length > 0)
533533
}
534534
const toLimit = (value: unknown): number | undefined =>
535-
value === undefined || value === null || value === '' ? undefined : Number(value)
535+
value === undefined || value === null || String(value).trim() === ''
536+
? undefined
537+
: Number(value)
538+
/** A dedicated limit wins when present, so a reference resolving to 0 still reaches validation. */
539+
const pickLimit = (dedicated: unknown, toolArg: unknown): unknown =>
540+
dedicated === undefined || dedicated === null || String(dedicated).trim() === ''
541+
? toolCallArg(context, toolArg)
542+
: dedicated
536543
const projectsArray = splitGids(params.projects)
537544

538545
// Only send a completion value when the user actually set the toggle; an
@@ -553,7 +560,7 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
553560
workspace: params.getTasks_workspace || toolCallArg(context, params.workspace),
554561
project: params.getTasks_project || toolCallArg(context, params.project),
555562
assignee: params.assignee,
556-
limit: toLimit(params.getTasks_limit || toolCallArg(context, params.limit)),
563+
limit: toLimit(pickLimit(params.getTasks_limit, params.limit)),
557564
offset: params.getTasks_offset || toolCallArg(context, params.offset),
558565
}
559566
case 'create_task':
@@ -588,7 +595,7 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
588595
assignee: params.assignee,
589596
projects: projectsArray,
590597
completed: completedValue,
591-
limit: toLimit(params.searchTasks_limit || toolCallArg(context, params.limit)),
598+
limit: toLimit(pickLimit(params.searchTasks_limit, params.limit)),
592599
}
593600
case 'add_comment':
594601
return {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { triggersMock } from '@sim/testing/mocks/triggers.mock'
2+
import { describe, expect, it, vi } from 'vitest'
3+
4+
vi.mock('@/triggers', () => triggersMock)
5+
6+
import { AttioBlock } from '@/blocks/blocks/attio'
7+
8+
describe('Attio agent operation resolution', () => {
9+
it('maps the selected list when the operation arrives only in the tool-call context', () => {
10+
const mapper = AttioBlock.tools.config?.params
11+
if (!mapper) throw new Error('Attio block has no params mapper')
12+
13+
const mapped = mapper(
14+
{ oauthCredential: 'cred', listIdOrSlug: 'list-1' },
15+
{ operation: 'get_list' }
16+
)
17+
18+
expect(mapped.list).toBe('list-1')
19+
})
20+
})

‎apps/sim/blocks/blocks/attio.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,11 +1609,11 @@ Record reference: {"allowed_objects": ["people", "companies"]}`,
16091609
],
16101610
config: {
16111611
tool: (params) => `attio_${params.operation}`,
1612-
params: (params) => {
1612+
params: (params, context) => {
16131613
const cleanParams: Record<string, unknown> = {
16141614
oauthCredential: params.oauthCredential,
16151615
}
1616-
const op = params.operation
1616+
const op = params.operation ?? context?.operation
16171617

16181618
// Record params — each field is only sent for the operations whose subBlock condition
16191619
// actually exposes it, so a stale value left over from switching operations never leaks

‎apps/sim/blocks/blocks/box.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,8 @@ export const BoxBlock = {
534534
if (normalizedFile) {
535535
params.file = normalizedFile
536536
}
537-
const { credential, operation, ...rest } = params
537+
const { credential, operation: storedOperation, ...rest } = params
538+
const operation = storedOperation ?? context?.operation
538539

539540
const baseParams: Record<string, unknown> = {
540541
accessToken: credential,
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { triggersMock } from '@sim/testing/mocks/triggers.mock'
2+
import { describe, expect, it, vi } from 'vitest'
3+
4+
vi.mock('@/triggers', () => triggersMock)
5+
6+
import { DropboxBlock } from '@/blocks/blocks/dropbox'
7+
8+
function requiredFor(subBlockId: string, values: Record<string, unknown>) {
9+
const subBlock = DropboxBlock.subBlocks.find(
10+
(candidate) => candidate.id === subBlockId && typeof candidate.required === 'function'
11+
)
12+
if (!subBlock || typeof subBlock.required !== 'function') {
13+
throw new Error(`${subBlockId} has no dynamic required condition`)
14+
}
15+
return subBlock.required(values)
16+
}
17+
18+
describe('Dropbox cursor continuation', () => {
19+
it('requires a folder path only until a list folder cursor is set', () => {
20+
expect(requiredFor('path', {})).toEqual({ field: 'operation', value: 'dropbox_list_folder' })
21+
expect(requiredFor('path', { listFolderCursor: ' ' })).toEqual({
22+
field: 'operation',
23+
value: 'dropbox_list_folder',
24+
})
25+
expect(requiredFor('path', { listFolderCursor: 'cursor-1' })).toEqual({
26+
field: 'operation',
27+
value: [],
28+
})
29+
})
30+
31+
it('requires a search query only until a search cursor is set', () => {
32+
expect(requiredFor('query', {})).toEqual({ field: 'operation', value: 'dropbox_search' })
33+
expect(requiredFor('query', { searchCursor: 'cursor-1' })).toEqual({
34+
field: 'operation',
35+
value: [],
36+
})
37+
})
38+
})

‎apps/sim/blocks/blocks/dropbox.ts‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,10 @@ export const DropboxBlock = {
175175
type: 'short-input',
176176
placeholder: '/ (root) or /folder',
177177
condition: { field: 'operation', value: 'dropbox_list_folder' },
178-
required: true,
178+
required: (values) =>
179+
String(values?.listFolderCursor ?? '').trim()
180+
? { field: 'operation', value: [] }
181+
: { field: 'operation', value: 'dropbox_list_folder' },
179182
},
180183
{
181184
id: 'recursive',
@@ -332,7 +335,10 @@ Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
332335
type: 'short-input',
333336
placeholder: 'Enter search term...',
334337
condition: { field: 'operation', value: 'dropbox_search' },
335-
required: true,
338+
required: (values) =>
339+
String(values?.searchCursor ?? '').trim()
340+
? { field: 'operation', value: [] }
341+
: { field: 'operation', value: 'dropbox_search' },
336342
},
337343
{
338344
id: 'path',

‎apps/sim/blocks/blocks/hubspot.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1635,7 +1635,7 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no
16351635
params: (params, context) => {
16361636
const {
16371637
oauthCredential,
1638-
operation,
1638+
operation: storedOperation,
16391639
propertiesToSet,
16401640
properties,
16411641
searchProperties,
@@ -1647,6 +1647,7 @@ Return ONLY the JSON array of property names - no explanations, no markdown, no
16471647
archived,
16481648
...rest
16491649
} = params
1650+
const operation = storedOperation ?? context?.operation
16501651

16511652
const cleanParams: Record<string, any> = {
16521653
oauthCredential,

0 commit comments

Comments
 (0)