Skip to content

Commit f2a6b36

Browse files
committed
fix(integrations): preserve request values and bound Slack reads
1 parent 81666bf commit f2a6b36

28 files changed

Lines changed: 664 additions & 172 deletions

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ Retrieve a specific message by its timestamp. Useful for getting a thread parent
343343

344344
### Slack Get Thread
345345

346-
Retrieve an entire thread including the parent message and all replies. Useful for getting full conversation context.
346+
Retrieve the first page of a thread, including the parent message and replies. Check hasMore for additional replies; use Get Thread Replies for cursor-based pagination.
347347

348348
#### Input
349349

@@ -469,7 +469,7 @@ Retrieve an entire thread including the parent message and all replies. Useful f
469469
| ↳ `edited` | object | Edit information if message was edited |
470470
| ↳ `user` | string | User ID who edited the message |
471471
| ↳ `ts` | string | Timestamp of the edit |
472-
| `messages` | array | All messages in the thread \(parent + replies\) in chronological order |
472+
| `messages` | array | Messages in this page \(parent + replies\) in chronological order |
473473
| ↳ `type` | string | Message type \(usually "message"\) |
474474
| ↳ `ts` | string | Message timestamp \(unique identifier\) |
475475
| ↳ `text` | string | Message text content |

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,7 @@ Update an existing subscription
10711071
| --------- | ---- | -------- | ----------- |
10721072
| `apiKey` | string | Yes | Stripe API key \(secret key\) |
10731073
| `id` | string | Yes | Subscription ID \(e.g., sub_1234567890\) |
1074-
| `items` | json | No | Updated array of items with price IDs |
1074+
| `items` | json | No | Items with price IDs and optional quantities. Include the existing subscription item id to replace its price; omit id to add an item. |
10751075
| `cancel_at_period_end` | boolean | No | Cancel subscription at period end |
10761076
| `metadata` | json | No | Updated metadata |
10771077

@@ -1222,7 +1222,7 @@ Cancel a subscription
12221222

12231223
### Stripe Resume Subscription
12241224

1225-
Resume a subscription that was scheduled for cancellation
1225+
Resume a paused subscription. To undo a scheduled cancellation, use Update Subscription with cancel_at_period_end=false before it ends.
12261226

12271227
#### Input
12281228

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,35 @@ describe.each([SlackBlock, SlackV2Block])('$type Block Kit fallback text', (bloc
252252
}
253253
)
254254
})
255+
256+
describe.each([SlackBlock, SlackV2Block])('$type channel target visibility', (block) => {
257+
it.each(['update', 'react', 'archive_conversation', 'get_channel_history', 'ephemeral'])(
258+
'keeps both channel inputs visible for %s after a DM action',
259+
(operation) => {
260+
for (const fieldId of ['channel', 'manualChannel']) {
261+
const field = block.subBlocks.find((candidate) => candidate.id === fieldId)
262+
if (!field) throw new Error(`Missing ${fieldId}`)
263+
expect(
264+
evaluateSubBlockCondition(field.condition, { operation, destinationType: 'dm' }),
265+
fieldId
266+
).toBe(true)
267+
}
268+
}
269+
)
270+
271+
it.each(['send', 'read', 'schedule_message'])(
272+
'preserves the channel/DM switch for %s in both modes',
273+
(operation) => {
274+
for (const fieldId of ['channel', 'manualChannel']) {
275+
const field = block.subBlocks.find((candidate) => candidate.id === fieldId)
276+
if (!field) throw new Error(`Missing ${fieldId}`)
277+
for (const destinationType of ['channel', 'dm']) {
278+
expect(
279+
evaluateSubBlockCondition(field.condition, { operation, destinationType }),
280+
`${fieldId} ${destinationType}`
281+
).toBe(destinationType === 'channel')
282+
}
283+
}
284+
}
285+
)
286+
})

‎apps/sim/blocks/blocks/slack.ts‎

Lines changed: 27 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,31 @@ const MESSAGE_BODY_FIELD = ['text', 'blocks'] as const
7272
*/
7373
const SLACK_TRIGGER_CHANNEL_FIELD = ['channelFilter', 'manualChannelFilter'] as const
7474

75+
function getSlackChannelCondition(values?: Record<string, unknown>) {
76+
if (DESTINATION_SWITCH_OPERATIONS.some((operation) => operation === values?.operation)) {
77+
return { field: 'destinationType', value: 'dm', not: true }
78+
}
79+
return {
80+
field: 'operation',
81+
value: [
82+
'list_channels',
83+
'list_users',
84+
'get_user',
85+
'get_user_presence',
86+
'edit_canvas',
87+
'get_canvas',
88+
'lookup_canvas_sections',
89+
'delete_canvas',
90+
'create_conversation',
91+
'open_view',
92+
'update_view',
93+
'push_view',
94+
'publish_view',
95+
],
96+
not: true,
97+
}
98+
}
99+
75100
export const SlackBlock: BlockConfig<SlackResponse> = {
76101
type: 'slack',
77102
name: 'Slack',
@@ -467,45 +492,7 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
467492
placeholder: 'Select Slack channel',
468493
mode: 'basic',
469494
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken', 'customBotCredential'] },
470-
condition: (values?: Record<string, unknown>) => {
471-
const op = values?.operation as string
472-
if (op === 'ephemeral') {
473-
return { field: 'operation', value: 'ephemeral' }
474-
}
475-
/*
476-
* Only the three operations that offer the channel/DM switch defer to
477-
* it. Deferring everywhere left a stale `destinationType: 'dm'` — set
478-
* under `send`, never cleared by an operation change — hiding the
479-
* channel field on operations that have no DM mode at all, so their
480-
* cards silently lost their only clause.
481-
*/
482-
if (DESTINATION_SWITCH_OPERATIONS.includes(op as never)) {
483-
return {
484-
field: 'destinationType',
485-
value: 'dm',
486-
not: true,
487-
}
488-
}
489-
return {
490-
field: 'operation',
491-
value: [
492-
'list_channels',
493-
'list_users',
494-
'get_user',
495-
'get_user_presence',
496-
'edit_canvas',
497-
'get_canvas',
498-
'lookup_canvas_sections',
499-
'delete_canvas',
500-
'create_conversation',
501-
'open_view',
502-
'update_view',
503-
'push_view',
504-
'publish_view',
505-
],
506-
not: true,
507-
}
508-
},
495+
condition: getSlackChannelCondition,
509496
required: {
510497
field: 'operation',
511498
value: ['list_canvases', 'list_scheduled_messages'],
@@ -520,36 +507,7 @@ export const SlackBlock: BlockConfig<SlackResponse> = {
520507
placeholder: 'Enter Slack channel ID (e.g., C1234567890)',
521508
dependsOn: { all: ['authMethod'], any: ['credential', 'botToken', 'customBotCredential'] },
522509
mode: 'advanced',
523-
condition: (values?: Record<string, unknown>) => {
524-
const op = values?.operation as string
525-
if (op === 'ephemeral') {
526-
return { field: 'operation', value: 'ephemeral' }
527-
}
528-
return {
529-
field: 'operation',
530-
value: [
531-
'list_channels',
532-
'list_users',
533-
'get_user',
534-
'get_user_presence',
535-
'edit_canvas',
536-
'get_canvas',
537-
'lookup_canvas_sections',
538-
'delete_canvas',
539-
'create_conversation',
540-
'open_view',
541-
'update_view',
542-
'push_view',
543-
'publish_view',
544-
],
545-
not: true,
546-
and: {
547-
field: 'destinationType',
548-
value: 'dm',
549-
not: true,
550-
},
551-
}
552-
},
510+
condition: getSlackChannelCondition,
553511
required: {
554512
field: 'operation',
555513
value: ['list_canvases', 'list_scheduled_messages'],

‎apps/sim/blocks/blocks/stripe.ts‎

Lines changed: 30 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { StripeIcon } from '@/components/icons'
22
import type { BlockConfig, BlockMeta } from '@/blocks/types'
33
import { AuthMode, IntegrationType } from '@/blocks/types'
4+
import {
5+
parseOptionalBooleanInput,
6+
parseOptionalJsonInput,
7+
parseOptionalNumberInput,
8+
} from '@/blocks/utils'
49
import type { StripeResponse } from '@/tools/stripe/types'
510
import { getTrigger } from '@/triggers'
611

@@ -457,7 +462,7 @@ export const StripeBlock: BlockConfig<StripeResponse> = {
457462
id: 'items',
458463
title: 'Items (JSON Array)',
459464
type: 'code',
460-
placeholder: '[{"price": "price_1234567890", "quantity": 1}]',
465+
placeholder: '[{"id": "si_1234567890", "price": "price_1234567890", "quantity": 1}]',
461466
condition: {
462467
field: 'operation',
463468
value: ['update_subscription'],
@@ -898,62 +903,30 @@ export const StripeBlock: BlockConfig<StripeResponse> = {
898903
return `stripe_${params.operation}`
899904
},
900905
params: (params) => {
901-
const {
902-
operation,
903-
apiKey,
904-
address,
905-
metadata,
906-
items,
907-
images,
908-
recurring,
909-
cancel_at_period_end,
910-
auto_advance,
911-
capture,
912-
active,
913-
prorate,
914-
invoice_now,
915-
paid_out_of_band,
916-
...rest
917-
} = params
918-
919-
// Parse JSON fields
920-
let parsedAddress: any | undefined
921-
let parsedMetadata: any | undefined
922-
let parsedItems: any | undefined
923-
let parsedImages: any | undefined
924-
let parsedRecurring: any | undefined
925-
926-
try {
927-
if (address) parsedAddress = JSON.parse(address)
928-
if (metadata) parsedMetadata = JSON.parse(metadata)
929-
if (items) parsedItems = JSON.parse(items)
930-
if (images) parsedImages = JSON.parse(images)
931-
if (recurring) parsedRecurring = JSON.parse(recurring)
932-
} catch (error: any) {
933-
throw new Error(`Invalid JSON input: ${error.message}`)
934-
}
935-
936-
// Convert string booleans to actual booleans
937-
const parsedBooleans: Record<string, boolean | undefined> = {}
938-
if (cancel_at_period_end !== undefined)
939-
parsedBooleans.cancel_at_period_end = cancel_at_period_end === 'true'
940-
if (auto_advance !== undefined) parsedBooleans.auto_advance = auto_advance === 'true'
941-
if (capture !== undefined) parsedBooleans.capture = capture === 'true'
942-
if (active !== undefined) parsedBooleans.active = active === 'true'
943-
if (prorate !== undefined) parsedBooleans.prorate = prorate === 'true'
944-
if (invoice_now !== undefined) parsedBooleans.invoice_now = invoice_now === 'true'
945-
if (paid_out_of_band !== undefined)
946-
parsedBooleans.paid_out_of_band = paid_out_of_band === 'true'
906+
const { operation, ...rest } = params
947907

948908
return {
949-
apiKey,
950909
...rest,
951-
...(parsedAddress && { address: parsedAddress }),
952-
...(parsedMetadata && { metadata: parsedMetadata }),
953-
...(parsedItems && { items: parsedItems }),
954-
...(parsedImages && { images: parsedImages }),
955-
...(parsedRecurring && { recurring: parsedRecurring }),
956-
...parsedBooleans,
910+
...(operation === 'capture_payment_intent' && {
911+
amount_to_capture:
912+
parseOptionalNumberInput(params.amount_to_capture, 'Amount to capture') ??
913+
parseOptionalNumberInput(params.amount, 'Amount'),
914+
}),
915+
...(operation === 'capture_charge' && {
916+
amount: parseOptionalNumberInput(params.amount, 'Amount'),
917+
}),
918+
address: parseOptionalJsonInput(params.address, 'Address'),
919+
metadata: parseOptionalJsonInput(params.metadata, 'Metadata'),
920+
items: parseOptionalJsonInput(params.items, 'Items'),
921+
images: parseOptionalJsonInput(params.images, 'Images'),
922+
recurring: parseOptionalJsonInput(params.recurring, 'Recurring'),
923+
cancel_at_period_end: parseOptionalBooleanInput(params.cancel_at_period_end),
924+
auto_advance: parseOptionalBooleanInput(params.auto_advance),
925+
capture: parseOptionalBooleanInput(params.capture),
926+
active: parseOptionalBooleanInput(params.active),
927+
prorate: parseOptionalBooleanInput(params.prorate),
928+
invoice_now: parseOptionalBooleanInput(params.invoice_now),
929+
paid_out_of_band: parseOptionalBooleanInput(params.paid_out_of_band),
957930
}
958931
},
959932
},
@@ -1127,15 +1100,15 @@ export const StripeBlockMeta = {
11271100
},
11281101
{
11291102
name: 'manage-subscription',
1130-
description: 'Create, update, pause, or cancel a Stripe subscription for a customer.',
1103+
description: 'Create, update, cancel, or resume a paused Stripe subscription for a customer.',
11311104
content:
1132-
'# Manage Subscription\n\nHandle the lifecycle of a recurring subscription.\n\n## Steps\n1. To start a subscription, run Create Subscription with the customer and price items.\n2. To change a plan, run Update Subscription with the new items. To pause and later restart, use Cancel Subscription or Resume Subscription as appropriate.\n3. Confirm the current state with Retrieve Subscription.\n\n## Output\nReturn the subscription ID, its status, current period end, and the plan items, and note exactly what changed.',
1105+
'# Manage Subscription\n\nHandle the lifecycle of a recurring subscription.\n\n## Steps\n1. To start a subscription, run Create Subscription with the customer and price items.\n2. To change a plan, run Update Subscription with the existing subscription item ID and replacement price. Omitting the item ID adds another item.\n3. To schedule cancellation, use Update Subscription with cancel_at_period_end=true; set it to false before the period ends to undo that schedule. Cancel Subscription ends the subscription immediately and cannot be undone. Resume Subscription applies only to paused subscriptions, not canceled ones.\n4. Confirm the current state with Retrieve Subscription.\n\n## Output\nReturn the subscription ID, status, and plan items, and note exactly what changed.',
11331106
},
11341107
{
11351108
name: 'issue-invoice',
11361109
description: 'Create, finalize, and send a Stripe invoice to a customer, then track payment.',
11371110
content:
1138-
'# Issue Invoice\n\nBill a customer with a Stripe invoice.\n\n## Steps\n1. Run Create Invoice for the customer with the line items.\n2. Run Finalize Invoice to lock it, then Send Invoice to deliver it to the customer.\n3. Track payment with Retrieve Invoice, or run Pay Invoice to charge a saved payment method. Use Void Invoice to cancel an unpaid invoice.\n\n## Output\nReturn the invoice ID, its status (draft, open, paid, or void), the amount due, and the hosted invoice URL when available.',
1111+
'# Issue Invoice\n\nBill a customer with a Stripe invoice.\n\n## Steps\n1. Run Create Invoice for the customer. This tool does not create line items; inspect the draft and any existing pending items with Retrieve Invoice.\n2. After verifying the amount and items, run Finalize Invoice to lock it, then Send Invoice to deliver it to the customer.\n3. Track payment with Retrieve Invoice, or run Pay Invoice to charge a saved payment method. Use Void Invoice to cancel an unpaid invoice.\n\n## Output\nReturn the invoice ID, its status (draft, open, paid, or void), the amount due, and the hosted invoice URL when available.',
11391112
},
11401113
{
11411114
name: 'find-customer-activity',

0 commit comments

Comments
 (0)