Skip to content

Commit bef36ec

Browse files
committed
improvement(resend): add error handling, authMode, and naming consistency
1 parent b42f80e commit bef36ec

File tree

11 files changed

+176
-49
lines changed

11 files changed

+176
-49
lines changed

apps/sim/blocks/blocks/resend.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ResendIcon } from '@/components/icons'
22
import type { BlockConfig } from '@/blocks/types'
3+
import { AuthMode } from '@/blocks/types'
34

45
export const ResendBlock: BlockConfig = {
56
type: 'resend',
@@ -11,23 +12,21 @@ export const ResendBlock: BlockConfig = {
1112
category: 'tools',
1213
bgColor: '#181C1E',
1314
icon: ResendIcon,
15+
authMode: AuthMode.ApiKey,
1416

1517
subBlocks: [
1618
{
1719
id: 'operation',
1820
title: 'Operation',
1921
type: 'dropdown',
2022
options: [
21-
// Email Operations
2223
{ label: 'Send Email', id: 'send_email' },
2324
{ label: 'Get Email', id: 'get_email' },
24-
// Contact Operations
2525
{ label: 'Create Contact', id: 'create_contact' },
2626
{ label: 'List Contacts', id: 'list_contacts' },
2727
{ label: 'Get Contact', id: 'get_contact' },
2828
{ label: 'Update Contact', id: 'update_contact' },
2929
{ label: 'Delete Contact', id: 'delete_contact' },
30-
// Domain Operations
3130
{ label: 'List Domains', id: 'list_domains' },
3231
],
3332
value: () => 'send_email',
@@ -41,7 +40,6 @@ export const ResendBlock: BlockConfig = {
4140
password: true,
4241
},
4342

44-
// Send Email fields
4543
{
4644
id: 'fromAddress',
4745
title: 'From Address',
@@ -80,7 +78,7 @@ export const ResendBlock: BlockConfig = {
8078
"Order confirmation" -> "Your Order #12345 is Confirmed"
8179
"Newsletter about new features" -> "New Features You'll Love"
8280
83-
Return ONLY the subject line - no explanations.`,
81+
Return ONLY the subject line - no explanations, no extra text.`,
8482
placeholder: 'Describe the email topic...',
8583
},
8684
},
@@ -100,7 +98,7 @@ Return ONLY the subject line - no explanations.`,
10098
- Keep paragraphs short
10199
- Include appropriate greeting and sign-off
102100
103-
Return ONLY the email body - no explanations.`,
101+
Return ONLY the email body - no explanations, no extra text.`,
104102
placeholder: 'Describe the email content...',
105103
},
106104
},
@@ -114,44 +112,62 @@ Return ONLY the email body - no explanations.`,
114112
],
115113
value: () => 'text',
116114
condition: { field: 'operation', value: 'send_email' },
115+
mode: 'advanced',
117116
},
118117
{
119118
id: 'cc',
120119
title: 'CC',
121120
type: 'short-input',
122121
placeholder: 'cc@example.com',
123122
condition: { field: 'operation', value: 'send_email' },
123+
mode: 'advanced',
124124
},
125125
{
126126
id: 'bcc',
127127
title: 'BCC',
128128
type: 'short-input',
129129
placeholder: 'bcc@example.com',
130130
condition: { field: 'operation', value: 'send_email' },
131+
mode: 'advanced',
131132
},
132133
{
133134
id: 'replyTo',
134135
title: 'Reply To',
135136
type: 'short-input',
136137
placeholder: 'reply@example.com',
137138
condition: { field: 'operation', value: 'send_email' },
139+
mode: 'advanced',
138140
},
139141
{
140142
id: 'scheduledAt',
141143
title: 'Schedule At',
142144
type: 'short-input',
143145
placeholder: '2024-08-05T11:52:01.858Z',
144146
condition: { field: 'operation', value: 'send_email' },
147+
mode: 'advanced',
148+
wandConfig: {
149+
enabled: true,
150+
generationType: 'timestamp',
151+
prompt:
152+
'Generate an ISO 8601 timestamp for scheduling email delivery. Return ONLY the timestamp - no explanations, no extra text.',
153+
placeholder: 'Describe when to send (e.g., "tomorrow at 9am")...',
154+
},
145155
},
146156
{
147157
id: 'tags',
148158
title: 'Tags',
149159
type: 'short-input',
150160
placeholder: 'category:welcome,type:onboarding',
151161
condition: { field: 'operation', value: 'send_email' },
162+
mode: 'advanced',
163+
wandConfig: {
164+
enabled: true,
165+
prompt:
166+
'Generate comma-separated key:value pairs for email tags based on the description. Example format: "category:welcome,type:onboarding". Return ONLY the tag pairs - no explanations, no extra text.',
167+
placeholder: 'Describe the email tags...',
168+
},
152169
},
153170

154-
// Get Email fields
155171
{
156172
id: 'emailId',
157173
title: 'Email ID',
@@ -161,7 +177,6 @@ Return ONLY the email body - no explanations.`,
161177
required: true,
162178
},
163179

164-
// Create Contact fields
165180
{
166181
id: 'email',
167182
title: 'Email',
@@ -196,7 +211,6 @@ Return ONLY the email body - no explanations.`,
196211
condition: { field: 'operation', value: ['create_contact', 'update_contact'] },
197212
},
198213

199-
// Get/Update/Delete Contact fields
200214
{
201215
id: 'contactId',
202216
title: 'Contact ID or Email',
@@ -239,7 +253,6 @@ Return ONLY the email body - no explanations.`,
239253
inputs: {
240254
operation: { type: 'string', description: 'Operation to perform' },
241255
resendApiKey: { type: 'string', description: 'Resend API key' },
242-
// Send email inputs
243256
fromAddress: { type: 'string', description: 'Email address to send from' },
244257
to: { type: 'string', description: 'Recipient email address' },
245258
subject: { type: 'string', description: 'Email subject' },
@@ -250,9 +263,7 @@ Return ONLY the email body - no explanations.`,
250263
replyTo: { type: 'string', description: 'Reply-to email address' },
251264
scheduledAt: { type: 'string', description: 'Scheduled send time in ISO 8601 format' },
252265
tags: { type: 'string', description: 'Email tags as key:value pairs' },
253-
// Get email inputs
254266
emailId: { type: 'string', description: 'Email ID to retrieve' },
255-
// Contact inputs
256267
email: { type: 'string', description: 'Contact email address' },
257268
firstName: { type: 'string', description: 'Contact first name' },
258269
lastName: { type: 'string', description: 'Contact last name' },
@@ -262,24 +273,22 @@ Return ONLY the email body - no explanations.`,
262273

263274
outputs: {
264275
success: { type: 'boolean', description: 'Operation success status' },
265-
// Send email outputs
276+
id: { type: 'string', description: 'Email or contact ID' },
266277
to: { type: 'string', description: 'Recipient email address' },
267278
subject: { type: 'string', description: 'Email subject' },
268279
body: { type: 'string', description: 'Email body content' },
269-
// Get email outputs
270-
id: { type: 'string', description: 'Email or contact ID' },
271280
from: { type: 'string', description: 'Sender email address' },
272281
html: { type: 'string', description: 'HTML email content' },
273282
text: { type: 'string', description: 'Plain text email content' },
274283
lastEvent: { type: 'string', description: 'Last event status' },
275284
createdAt: { type: 'string', description: 'Creation timestamp' },
285+
scheduledAt: { type: 'string', description: 'Scheduled send timestamp' },
276286
tags: { type: 'json', description: 'Email tags as name-value pairs' },
277-
// Contact outputs
278287
email: { type: 'string', description: 'Contact email address' },
279288
firstName: { type: 'string', description: 'Contact first name' },
280289
lastName: { type: 'string', description: 'Contact last name' },
290+
unsubscribed: { type: 'boolean', description: 'Whether the contact is unsubscribed' },
281291
contacts: { type: 'json', description: 'Array of contacts' },
282-
// Domain outputs
283292
domains: { type: 'json', description: 'Array of domains' },
284293
hasMore: { type: 'boolean', description: 'Whether more results are available' },
285294
deleted: { type: 'boolean', description: 'Whether the resource was deleted' },

apps/sim/tools/registry.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,13 +1599,13 @@ import {
15991599
} from '@/tools/redis'
16001600
import { reductoParserTool, reductoParserV2Tool } from '@/tools/reducto'
16011601
import {
1602-
mailSendTool,
16031602
resendCreateContactTool,
16041603
resendDeleteContactTool,
16051604
resendGetContactTool,
16061605
resendGetEmailTool,
16071606
resendListContactsTool,
16081607
resendListDomainsTool,
1608+
resendSendTool,
16091609
resendUpdateContactTool,
16101610
} from '@/tools/resend'
16111611
import {
@@ -2390,7 +2390,7 @@ export const tools: Record<string, ToolConfig> = {
23902390
luma_update_event: lumaUpdateEventTool,
23912391
linkedin_share_post: linkedInSharePostTool,
23922392
linkedin_get_profile: linkedInGetProfileTool,
2393-
resend_send: mailSendTool,
2393+
resend_send: resendSendTool,
23942394
resend_get_email: resendGetEmailTool,
23952395
resend_create_contact: resendCreateContactTool,
23962396
resend_list_contacts: resendListContactsTool,

apps/sim/tools/resend/create_contact.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import { createLogger } from '@sim/logger'
12
import type { CreateContactParams, CreateContactResult } from '@/tools/resend/types'
23
import type { ToolConfig } from '@/tools/types'
34

5+
const logger = createLogger('ResendCreateContactTool')
6+
47
export const resendCreateContactTool: ToolConfig<CreateContactParams, CreateContactResult> = {
58
id: 'resend_create_contact',
69
name: 'Create Contact',
@@ -58,6 +61,17 @@ export const resendCreateContactTool: ToolConfig<CreateContactParams, CreateCont
5861
transformResponse: async (response: Response): Promise<CreateContactResult> => {
5962
const data = await response.json()
6063

64+
if (!data.id) {
65+
logger.error('Resend Create Contact API error:', JSON.stringify(data, null, 2))
66+
return {
67+
success: false,
68+
error: data.message || 'Failed to create contact',
69+
output: {
70+
id: '',
71+
},
72+
}
73+
}
74+
6175
return {
6276
success: true,
6377
output: {

apps/sim/tools/resend/delete_contact.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import { createLogger } from '@sim/logger'
12
import type { DeleteContactParams, DeleteContactResult } from '@/tools/resend/types'
23
import type { ToolConfig } from '@/tools/types'
34

5+
const logger = createLogger('ResendDeleteContactTool')
6+
47
export const resendDeleteContactTool: ToolConfig<DeleteContactParams, DeleteContactResult> = {
58
id: 'resend_delete_contact',
69
name: 'Delete Contact',
@@ -24,7 +27,7 @@ export const resendDeleteContactTool: ToolConfig<DeleteContactParams, DeleteCont
2427

2528
request: {
2629
url: (params: DeleteContactParams) =>
27-
`https://api.resend.com/contacts/${encodeURIComponent(params.contactId)}`,
30+
`https://api.resend.com/contacts/${encodeURIComponent(params.contactId.trim())}`,
2831
method: 'DELETE',
2932
headers: (params: DeleteContactParams) => ({
3033
Authorization: `Bearer ${params.resendApiKey}`,
@@ -35,10 +38,22 @@ export const resendDeleteContactTool: ToolConfig<DeleteContactParams, DeleteCont
3538
transformResponse: async (response: Response): Promise<DeleteContactResult> => {
3639
const data = await response.json()
3740

41+
if (data.message && !data.deleted) {
42+
logger.error('Resend Delete Contact API error:', JSON.stringify(data, null, 2))
43+
return {
44+
success: false,
45+
error: data.message || 'Failed to delete contact',
46+
output: {
47+
id: '',
48+
deleted: false,
49+
},
50+
}
51+
}
52+
3853
return {
3954
success: true,
4055
output: {
41-
id: data.contact || data.id || '',
56+
id: data.contact ?? data.id ?? '',
4257
deleted: data.deleted ?? true,
4358
},
4459
}

apps/sim/tools/resend/get_contact.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import { createLogger } from '@sim/logger'
12
import type { GetContactParams, GetContactResult } from '@/tools/resend/types'
23
import type { ToolConfig } from '@/tools/types'
34

5+
const logger = createLogger('ResendGetContactTool')
6+
47
export const resendGetContactTool: ToolConfig<GetContactParams, GetContactResult> = {
58
id: 'resend_get_contact',
69
name: 'Get Contact',
@@ -24,7 +27,7 @@ export const resendGetContactTool: ToolConfig<GetContactParams, GetContactResult
2427

2528
request: {
2629
url: (params: GetContactParams) =>
27-
`https://api.resend.com/contacts/${encodeURIComponent(params.contactId)}`,
30+
`https://api.resend.com/contacts/${encodeURIComponent(params.contactId.trim())}`,
2831
method: 'GET',
2932
headers: (params: GetContactParams) => ({
3033
Authorization: `Bearer ${params.resendApiKey}`,
@@ -35,15 +38,31 @@ export const resendGetContactTool: ToolConfig<GetContactParams, GetContactResult
3538
transformResponse: async (response: Response): Promise<GetContactResult> => {
3639
const data = await response.json()
3740

41+
if (!data.id) {
42+
logger.error('Resend Get Contact API error:', JSON.stringify(data, null, 2))
43+
return {
44+
success: false,
45+
error: data.message || 'Failed to retrieve contact',
46+
output: {
47+
id: '',
48+
email: '',
49+
firstName: '',
50+
lastName: '',
51+
createdAt: '',
52+
unsubscribed: false,
53+
},
54+
}
55+
}
56+
3857
return {
3958
success: true,
4059
output: {
4160
id: data.id,
42-
email: data.email,
43-
firstName: data.first_name || '',
44-
lastName: data.last_name || '',
45-
createdAt: data.created_at || '',
46-
unsubscribed: data.unsubscribed || false,
61+
email: data.email ?? '',
62+
firstName: data.first_name ?? '',
63+
lastName: data.last_name ?? '',
64+
createdAt: data.created_at ?? '',
65+
unsubscribed: data.unsubscribed ?? false,
4766
},
4867
}
4968
},

0 commit comments

Comments
 (0)