From 66a247f92f71fe8039a1f71d3f5532f480ee7309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Melo?= Date: Wed, 22 Jan 2025 21:57:36 -0300 Subject: [PATCH 1/5] fix: correct email batch api call and update version - update version from 4.1.1 to 4.2.0 - fix /emails/batch api call to use reply_to instead of replyTo - refactor email transformation using parseEmailToAPIOptions in batch and emails - export Attachment interface in create-email-options.interface.ts --- package.json | 2 +- src/batch/batch.ts | 8 +++- .../interfaces/email-api-options.interface.ts | 18 ++++++++ .../utils/parse-email-to-api-options.spec.ts | 44 +++++++++++++++++++ .../utils/parse-email-to-api-options.ts | 21 +++++++++ src/emails/emails.ts | 16 +------ .../create-email-options.interface.ts | 2 +- 7 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 src/common/interfaces/email-api-options.interface.ts create mode 100644 src/common/utils/parse-email-to-api-options.spec.ts create mode 100644 src/common/utils/parse-email-to-api-options.ts diff --git a/package.json b/package.json index f0ade031..9f43f70f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "resend", - "version": "4.1.1", + "version": "4.2.0", "description": "Node.js library for the Resend API", "main": "./dist/index.js", "module": "./dist/index.mjs", diff --git a/src/batch/batch.ts b/src/batch/batch.ts index e0dc97d0..66b10b56 100644 --- a/src/batch/batch.ts +++ b/src/batch/batch.ts @@ -1,4 +1,6 @@ import type * as React from 'react'; +import type { EmailAPIOptions } from '../common/interfaces/email-api-options.interface'; +import { parseEmailToAPIOptions } from '../common/utils/parse-email-to-api-options'; import type { Resend } from '../resend'; import type { CreateBatchOptions, @@ -22,6 +24,8 @@ export class Batch { payload: CreateBatchOptions, options: CreateBatchRequestOptions = {}, ): Promise { + const emails: EmailAPIOptions[] = []; + for (const email of payload) { if (email.react) { if (!this.renderAsync) { @@ -38,11 +42,13 @@ export class Batch { email.html = await this.renderAsync(email.react as React.ReactElement); email.react = undefined; } + + emails.push(parseEmailToAPIOptions(email)); } const data = await this.resend.post( '/emails/batch', - payload, + emails, options, ); diff --git a/src/common/interfaces/email-api-options.interface.ts b/src/common/interfaces/email-api-options.interface.ts new file mode 100644 index 00000000..d0e7c440 --- /dev/null +++ b/src/common/interfaces/email-api-options.interface.ts @@ -0,0 +1,18 @@ +import type { Attachment } from '../../emails/interfaces/create-email-options.interface'; +import type { Tag } from '../../interfaces'; + +export interface EmailAPIOptions { + from: string; + to: string | string[]; + subject: string; + region?: string; + headers?: Record; + html?: string; + text?: string; + bcc?: string | string[]; + cc?: string | string[]; + reply_to?: string | string[]; + scheduled_at?: string; + tags?: Tag[]; + attachments?: Attachment[]; +} diff --git a/src/common/utils/parse-email-to-api-options.spec.ts b/src/common/utils/parse-email-to-api-options.spec.ts new file mode 100644 index 00000000..709c5b45 --- /dev/null +++ b/src/common/utils/parse-email-to-api-options.spec.ts @@ -0,0 +1,44 @@ +import type { CreateEmailOptions } from '../../emails/interfaces/create-email-options.interface'; +import { parseEmailToAPIOptions } from './parse-email-to-api-options'; + +describe('parseEmailToAPIOptions', () => { + it('should handle minimal email with only required fields', () => { + const emailPayload: CreateEmailOptions = { + from: 'joao@resend.com', + to: 'bu@resend.com', + subject: 'Hey, there!', + html: '

Hey, there!

', + }; + + const apiOptions = parseEmailToAPIOptions(emailPayload); + + expect(apiOptions).toEqual({ + from: 'joao@resend.com', + to: 'bu@resend.com', + subject: 'Hey, there!', + html: '

Hey, there!

', + }); + }); + + it('should properly parse camel case to snake case', () => { + const emailPayload: CreateEmailOptions = { + from: 'joao@resend.com', + to: 'bu@resend.com', + subject: 'Hey, there!', + html: '

Hey, there!

', + replyTo: 'zeno@resend.com', + scheduledAt: 'in 1 min', + }; + + const apiOptions = parseEmailToAPIOptions(emailPayload); + + expect(apiOptions).toEqual({ + from: 'joao@resend.com', + to: 'bu@resend.com', + subject: 'Hey, there!', + html: '

Hey, there!

', + reply_to: 'zeno@resend.com', + scheduled_at: 'in 1 min', + }); + }); +}); diff --git a/src/common/utils/parse-email-to-api-options.ts b/src/common/utils/parse-email-to-api-options.ts new file mode 100644 index 00000000..afca279e --- /dev/null +++ b/src/common/utils/parse-email-to-api-options.ts @@ -0,0 +1,21 @@ +import type { CreateEmailOptions } from '../../emails/interfaces/create-email-options.interface'; +import type { EmailAPIOptions } from '../interfaces/email-api-options.interface'; + +export function parseEmailToAPIOptions( + email: CreateEmailOptions, +): EmailAPIOptions { + return { + attachments: email.attachments, + bcc: email.bcc, + cc: email.cc, + from: email.from, + headers: email.headers, + html: email.html, + reply_to: email.replyTo, + scheduled_at: email.scheduledAt, + subject: email.subject, + tags: email.tags, + text: email.text, + to: email.to, + }; +} diff --git a/src/emails/emails.ts b/src/emails/emails.ts index 952aff3b..0b4e992f 100644 --- a/src/emails/emails.ts +++ b/src/emails/emails.ts @@ -1,4 +1,5 @@ import type * as React from 'react'; +import { parseEmailToAPIOptions } from '../common/utils/parse-email-to-api-options'; import type { Resend } from '../resend'; import type { CancelEmailResponse, @@ -54,20 +55,7 @@ export class Emails { const data = await this.resend.post( '/emails', - { - attachments: payload.attachments, - bcc: payload.bcc, - cc: payload.cc, - from: payload.from, - headers: payload.headers, - html: payload.html, - reply_to: payload.replyTo, - scheduled_at: payload.scheduledAt, - subject: payload.subject, - tags: payload.tags, - text: payload.text, - to: payload.to, - }, + parseEmailToAPIOptions(payload), options, ); diff --git a/src/emails/interfaces/create-email-options.interface.ts b/src/emails/interfaces/create-email-options.interface.ts index 0e4a5921..f76d561e 100644 --- a/src/emails/interfaces/create-email-options.interface.ts +++ b/src/emails/interfaces/create-email-options.interface.ts @@ -103,7 +103,7 @@ export interface CreateEmailResponse { error: ErrorResponse | null; } -interface Attachment { +export interface Attachment { /** Content of an attached file. */ content?: string | Buffer; /** Name of attached file. */ From 5a566c0809490f97eb7c06466f224143d54befbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Melo?= Date: Wed, 22 Jan 2025 22:14:23 -0300 Subject: [PATCH 2/5] revert: downgrade package version to 4.1.1 - changed version from 4.2.0 to 4.1.1 in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9f43f70f..f0ade031 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "resend", - "version": "4.2.0", + "version": "4.1.1", "description": "Node.js library for the Resend API", "main": "./dist/index.js", "module": "./dist/index.mjs", From 3f637f386591a44e9ff996a7c3d4615cd5285ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Melo?= Date: Wed, 22 Jan 2025 22:15:05 -0300 Subject: [PATCH 3/5] refactor: rename EmailAPIOptions to EmailApiOptions for consistency - updated type import in batch.ts - modified interface name in email-api-options.interface.ts - adjusted function return type in parse-email-to-api-options.ts --- src/batch/batch.ts | 4 ++-- src/common/interfaces/email-api-options.interface.ts | 2 +- src/common/utils/parse-email-to-api-options.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/batch/batch.ts b/src/batch/batch.ts index 66b10b56..fdacc5e4 100644 --- a/src/batch/batch.ts +++ b/src/batch/batch.ts @@ -1,5 +1,5 @@ import type * as React from 'react'; -import type { EmailAPIOptions } from '../common/interfaces/email-api-options.interface'; +import type { EmailApiOptions } from '../common/interfaces/email-api-options.interface'; import { parseEmailToAPIOptions } from '../common/utils/parse-email-to-api-options'; import type { Resend } from '../resend'; import type { @@ -24,7 +24,7 @@ export class Batch { payload: CreateBatchOptions, options: CreateBatchRequestOptions = {}, ): Promise { - const emails: EmailAPIOptions[] = []; + const emails: EmailApiOptions[] = []; for (const email of payload) { if (email.react) { diff --git a/src/common/interfaces/email-api-options.interface.ts b/src/common/interfaces/email-api-options.interface.ts index d0e7c440..f13a3b85 100644 --- a/src/common/interfaces/email-api-options.interface.ts +++ b/src/common/interfaces/email-api-options.interface.ts @@ -1,7 +1,7 @@ import type { Attachment } from '../../emails/interfaces/create-email-options.interface'; import type { Tag } from '../../interfaces'; -export interface EmailAPIOptions { +export interface EmailApiOptions { from: string; to: string | string[]; subject: string; diff --git a/src/common/utils/parse-email-to-api-options.ts b/src/common/utils/parse-email-to-api-options.ts index afca279e..577a7e58 100644 --- a/src/common/utils/parse-email-to-api-options.ts +++ b/src/common/utils/parse-email-to-api-options.ts @@ -1,9 +1,9 @@ import type { CreateEmailOptions } from '../../emails/interfaces/create-email-options.interface'; -import type { EmailAPIOptions } from '../interfaces/email-api-options.interface'; +import type { EmailApiOptions } from '../interfaces/email-api-options.interface'; export function parseEmailToAPIOptions( email: CreateEmailOptions, -): EmailAPIOptions { +): EmailApiOptions { return { attachments: email.attachments, bcc: email.bcc, From f2e0d8868ff6481643a9006eac25658e7a67ee7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Melo?= Date: Wed, 22 Jan 2025 22:15:48 -0300 Subject: [PATCH 4/5] fix: correct function name casing from parseEmailToAPIOptions to parseEmailToApiOptions - updated function name in imports across multiple files - modified function name in test cases - ensured consistent naming convention throughout the codebase --- src/batch/batch.ts | 4 ++-- src/common/utils/parse-email-to-api-options.spec.ts | 6 +++--- src/common/utils/parse-email-to-api-options.ts | 2 +- src/emails/emails.ts | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/batch/batch.ts b/src/batch/batch.ts index fdacc5e4..18829170 100644 --- a/src/batch/batch.ts +++ b/src/batch/batch.ts @@ -1,6 +1,6 @@ import type * as React from 'react'; import type { EmailApiOptions } from '../common/interfaces/email-api-options.interface'; -import { parseEmailToAPIOptions } from '../common/utils/parse-email-to-api-options'; +import { parseEmailToApiOptions } from '../common/utils/parse-email-to-api-options'; import type { Resend } from '../resend'; import type { CreateBatchOptions, @@ -43,7 +43,7 @@ export class Batch { email.react = undefined; } - emails.push(parseEmailToAPIOptions(email)); + emails.push(parseEmailToApiOptions(email)); } const data = await this.resend.post( diff --git a/src/common/utils/parse-email-to-api-options.spec.ts b/src/common/utils/parse-email-to-api-options.spec.ts index 709c5b45..983a5f85 100644 --- a/src/common/utils/parse-email-to-api-options.spec.ts +++ b/src/common/utils/parse-email-to-api-options.spec.ts @@ -1,5 +1,5 @@ import type { CreateEmailOptions } from '../../emails/interfaces/create-email-options.interface'; -import { parseEmailToAPIOptions } from './parse-email-to-api-options'; +import { parseEmailToApiOptions } from './parse-email-to-api-options'; describe('parseEmailToAPIOptions', () => { it('should handle minimal email with only required fields', () => { @@ -10,7 +10,7 @@ describe('parseEmailToAPIOptions', () => { html: '

Hey, there!

', }; - const apiOptions = parseEmailToAPIOptions(emailPayload); + const apiOptions = parseEmailToApiOptions(emailPayload); expect(apiOptions).toEqual({ from: 'joao@resend.com', @@ -30,7 +30,7 @@ describe('parseEmailToAPIOptions', () => { scheduledAt: 'in 1 min', }; - const apiOptions = parseEmailToAPIOptions(emailPayload); + const apiOptions = parseEmailToApiOptions(emailPayload); expect(apiOptions).toEqual({ from: 'joao@resend.com', diff --git a/src/common/utils/parse-email-to-api-options.ts b/src/common/utils/parse-email-to-api-options.ts index 577a7e58..cd935ace 100644 --- a/src/common/utils/parse-email-to-api-options.ts +++ b/src/common/utils/parse-email-to-api-options.ts @@ -1,7 +1,7 @@ import type { CreateEmailOptions } from '../../emails/interfaces/create-email-options.interface'; import type { EmailApiOptions } from '../interfaces/email-api-options.interface'; -export function parseEmailToAPIOptions( +export function parseEmailToApiOptions( email: CreateEmailOptions, ): EmailApiOptions { return { diff --git a/src/emails/emails.ts b/src/emails/emails.ts index 0b4e992f..23c13b0c 100644 --- a/src/emails/emails.ts +++ b/src/emails/emails.ts @@ -1,5 +1,5 @@ import type * as React from 'react'; -import { parseEmailToAPIOptions } from '../common/utils/parse-email-to-api-options'; +import { parseEmailToApiOptions } from '../common/utils/parse-email-to-api-options'; import type { Resend } from '../resend'; import type { CancelEmailResponse, @@ -55,7 +55,7 @@ export class Emails { const data = await this.resend.post( '/emails', - parseEmailToAPIOptions(payload), + parseEmailToApiOptions(payload), options, ); From ddc7338aa7f9ac7830a166e97a7572647cdef1fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Melo?= Date: Wed, 22 Jan 2025 22:16:03 -0300 Subject: [PATCH 5/5] fix: correct test description casing in parse-email-to-api-options spec - change describe block to use correct camelCase for function name --- src/common/utils/parse-email-to-api-options.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/utils/parse-email-to-api-options.spec.ts b/src/common/utils/parse-email-to-api-options.spec.ts index 983a5f85..9e1b7350 100644 --- a/src/common/utils/parse-email-to-api-options.spec.ts +++ b/src/common/utils/parse-email-to-api-options.spec.ts @@ -1,7 +1,7 @@ import type { CreateEmailOptions } from '../../emails/interfaces/create-email-options.interface'; import { parseEmailToApiOptions } from './parse-email-to-api-options'; -describe('parseEmailToAPIOptions', () => { +describe('parseEmailToApiOptions', () => { it('should handle minimal email with only required fields', () => { const emailPayload: CreateEmailOptions = { from: 'joao@resend.com',