diff --git a/src/batch/batch.spec.ts b/src/batch/batch.spec.ts new file mode 100644 index 00000000..acd95706 --- /dev/null +++ b/src/batch/batch.spec.ts @@ -0,0 +1,96 @@ +import { enableFetchMocks } from 'jest-fetch-mock'; +import { Resend } from '../resend'; + +enableFetchMocks(); + +const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop'); + +describe('Batch', () => { + afterEach(() => fetchMock.resetMocks()); + + describe('create', () => { + it('sends multiple emails', async () => { + const payload = [ + { + from: 'bu@resend.com', + to: 'zeno@resend.com', + subject: 'Hello World', + html: '

Hello world

', + }, + { + from: 'vitor@resend.com', + to: 'zeno@resend.com', + subject: 'Olá mundo', + html: '

olá mundo

', + }, + { + from: 'bu@resend.com', + to: 'vitor@resend.com', + subject: 'Hi there', + html: '

Hi there

', + }, + ]; + + fetchMock.mockOnce( + JSON.stringify({ + data: [{ id: '1234' }, { id: '4567' }, { id: '4242' }], + }), + { + status: 200, + headers: { + 'content-type': 'application/json', + Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', + }, + }, + ); + + const data = await resend.batch.create(payload); + expect(data).toMatchObject({ + data: [{ id: '1234' }, { id: '4567' }, { id: '4242' }], + }); + }); + }); + + describe('send', () => { + it('sends multiple emails', async () => { + const payload = [ + { + from: 'bu@resend.com', + to: 'zeno@resend.com', + subject: 'Hello World', + html: '

Hello world

', + }, + { + from: 'vitor@resend.com', + to: 'zeno@resend.com', + subject: 'Olá mundo', + html: '

olá mundo

', + }, + { + from: 'bu@resend.com', + to: 'vitor@resend.com', + subject: 'Hi there', + html: '

Hi there

', + }, + ]; + + fetchMock.mockOnce( + JSON.stringify({ + data: [{ id: '1234' }, { id: '4567' }, { id: '4242' }], + }), + { + status: 200, + headers: { + 'content-type': 'application/json', + Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop', + }, + }, + ); + + const data = await resend.batch.send(payload); + expect(data).toMatchObject({ + data: [{ id: '1234' }, { id: '4567' }, { id: '4242' }], + }); + }); + }); +}); diff --git a/src/batch/batch.ts b/src/batch/batch.ts new file mode 100644 index 00000000..bc895856 --- /dev/null +++ b/src/batch/batch.ts @@ -0,0 +1,39 @@ +import { render } from '@react-email/render'; +import * as React from 'react'; +import { Resend } from '../resend'; +import { + CreateBatchOptions, + CreateBatchRequestOptions, + CreateBatchResponse, +} from './interfaces'; + +export class Batch { + constructor(private readonly resend: Resend) {} + + async send( + payload: CreateBatchOptions, + options: CreateBatchRequestOptions = {}, + ): Promise { + return this.create(payload, options); + } + + async create( + payload: CreateBatchOptions, + options: CreateBatchRequestOptions = {}, + ): Promise { + for (const email of payload) { + if (email.react) { + email.html = render(email.react as React.ReactElement); + delete email.react; + } + } + + const data = await this.resend.post( + '/emails/batch', + payload, + options, + ); + + return data; + } +} diff --git a/src/batch/interfaces/create-batch-options.interface.ts b/src/batch/interfaces/create-batch-options.interface.ts new file mode 100644 index 00000000..17e591e0 --- /dev/null +++ b/src/batch/interfaces/create-batch-options.interface.ts @@ -0,0 +1,13 @@ +import { PostOptions } from '../../common/interfaces'; +import { CreateEmailOptions } from '../../emails/interfaces/create-email-options.interface'; + +export type CreateBatchOptions = CreateEmailOptions[]; + +export interface CreateBatchRequestOptions extends PostOptions {} + +export interface CreateBatchResponse { + data: { + /** The ID of the newly created email. */ + id: string; + }[]; +} diff --git a/src/batch/interfaces/index.ts b/src/batch/interfaces/index.ts new file mode 100644 index 00000000..306bcd76 --- /dev/null +++ b/src/batch/interfaces/index.ts @@ -0,0 +1 @@ +export * from './create-batch-options.interface'; diff --git a/src/resend.ts b/src/resend.ts index 9b9c1aa5..7310262d 100644 --- a/src/resend.ts +++ b/src/resend.ts @@ -1,8 +1,7 @@ -import * as React from 'react'; -import { render } from '@react-email/render'; import { version } from '../package.json'; -import { GetOptions, PostOptions, PutOptions } from './common/interfaces'; import { ApiKeys } from './api-keys/api-keys'; +import { Batch } from './batch/batch'; +import { GetOptions, PostOptions, PutOptions } from './common/interfaces'; import { Domains } from './domains/domains'; import { Emails } from './emails/emails'; import { CreateEmailOptions, CreateEmailResponse } from './emails/interfaces'; @@ -18,6 +17,7 @@ export class Resend { readonly apiKeys = new ApiKeys(this); readonly domains = new Domains(this); readonly emails = new Emails(this); + readonly batch = new Batch(this); constructor(readonly key?: string) { if (!key) { @@ -100,27 +100,7 @@ export class Resend { return await this.fetchRequest(path, requestOptions); } - async sendEmail(data: CreateEmailOptions) { - const path = '/email'; - - if (data.react) { - data.html = render(data.react as React.ReactElement); - delete data.react; - } - - const response = await this.post(path, { - from: data.from, - to: data.to, - bcc: data.bcc, - cc: data.cc, - reply_to: data.reply_to, - subject: data.subject, - text: data.text, - html: data.html, - attachments: data.attachments, - tags: data.tags, - }); - - return response; + async sendEmail(data: CreateEmailOptions): Promise { + return this.emails.create(data); } }