Skip to content

Commit 06bb717

Browse files
authored
feat: Add get, remove and list broadcast endpoints (#453)
1 parent 4dc45a7 commit 06bb717

7 files changed

+279
-0
lines changed

src/broadcasts/broadcasts.spec.ts

+186
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import type {
55
CreateBroadcastOptions,
66
CreateBroadcastResponseSuccess,
77
} from './interfaces/create-broadcast-options.interface';
8+
import type { GetBroadcastResponseSuccess } from './interfaces/get-broadcast.interface';
9+
import type { ListBroadcastsResponseSuccess } from './interfaces/list-broadcasts.interface';
10+
import type { RemoveBroadcastResponseSuccess } from './interfaces/remove-broadcast.interface';
811

912
enableFetchMocks();
1013

@@ -246,6 +249,189 @@ describe('Broadcasts', () => {
246249
},
247250
"error": null,
248251
}
252+
`);
253+
});
254+
});
255+
256+
describe('list', () => {
257+
it('lists broadcasts', async () => {
258+
const response: ListBroadcastsResponseSuccess = {
259+
object: 'list',
260+
data: [
261+
{
262+
id: '49a3999c-0ce1-4ea6-ab68-afcd6dc2e794',
263+
audience_id: '78261eea-8f8b-4381-83c6-79fa7120f1cf',
264+
name: 'broadcast 1',
265+
status: 'draft',
266+
created_at: '2024-11-01T15:13:31.723Z',
267+
scheduled_at: null,
268+
sent_at: null,
269+
},
270+
{
271+
id: '559ac32e-9ef5-46fb-82a1-b76b840c0f7b',
272+
audience_id: '78261eea-8f8b-4381-83c6-79fa7120f1cf',
273+
name: 'broadcast 2',
274+
status: 'sent',
275+
created_at: '2024-12-01T19:32:22.980Z',
276+
scheduled_at: '2024-12-02T19:32:22.980Z',
277+
sent_at: '2024-12-02T19:32:22.980Z',
278+
},
279+
],
280+
};
281+
fetchMock.mockOnce(JSON.stringify(response), {
282+
status: 200,
283+
headers: {
284+
'content-type': 'application/json',
285+
Authorization: 'Bearer re_924b3rjh2387fbewf823',
286+
},
287+
});
288+
289+
const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop');
290+
291+
await expect(resend.broadcasts.list()).resolves.toMatchInlineSnapshot(`
292+
{
293+
"data": {
294+
"data": [
295+
{
296+
"audience_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
297+
"created_at": "2024-11-01T15:13:31.723Z",
298+
"id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794",
299+
"name": "broadcast 1",
300+
"scheduled_at": null,
301+
"sent_at": null,
302+
"status": "draft",
303+
},
304+
{
305+
"audience_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
306+
"created_at": "2024-12-01T19:32:22.980Z",
307+
"id": "559ac32e-9ef5-46fb-82a1-b76b840c0f7b",
308+
"name": "broadcast 2",
309+
"scheduled_at": "2024-12-02T19:32:22.980Z",
310+
"sent_at": "2024-12-02T19:32:22.980Z",
311+
"status": "sent",
312+
},
313+
],
314+
"object": "list",
315+
},
316+
"error": null,
317+
}
318+
`);
319+
});
320+
});
321+
322+
describe('get', () => {
323+
describe('when broadcast not found', () => {
324+
it('returns error', async () => {
325+
const response: ErrorResponse = {
326+
name: 'not_found',
327+
message: 'Broadcast not found',
328+
};
329+
330+
fetchMock.mockOnce(JSON.stringify(response), {
331+
status: 404,
332+
headers: {
333+
'content-type': 'application/json',
334+
Authorization: 'Bearer re_924b3rjh2387fbewf823',
335+
},
336+
});
337+
338+
const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop');
339+
340+
const result = resend.broadcasts.get(
341+
'559ac32e-9ef5-46fb-82a1-b76b840c0f7b',
342+
);
343+
344+
await expect(result).resolves.toMatchInlineSnapshot(`
345+
{
346+
"data": null,
347+
"error": {
348+
"message": "Broadcast not found",
349+
"name": "not_found",
350+
},
351+
}
352+
`);
353+
});
354+
});
355+
356+
it('get broadcast', async () => {
357+
const response: GetBroadcastResponseSuccess = {
358+
object: 'broadcast',
359+
id: '559ac32e-9ef5-46fb-82a1-b76b840c0f7b',
360+
name: 'Announcements',
361+
audience_id: '78261eea-8f8b-4381-83c6-79fa7120f1cf',
362+
from: 'Acme <[email protected]>',
363+
subject: 'hello world',
364+
reply_to: null,
365+
preview_text: 'Check out our latest announcements',
366+
status: 'draft',
367+
created_at: '2024-12-01T19:32:22.980Z',
368+
scheduled_at: null,
369+
sent_at: null,
370+
};
371+
372+
fetchMock.mockOnce(JSON.stringify(response), {
373+
status: 200,
374+
headers: {
375+
'content-type': 'application/json',
376+
Authorization: 'Bearer re_924b3rjh2387fbewf823',
377+
},
378+
});
379+
380+
const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop');
381+
382+
await expect(
383+
resend.broadcasts.get('559ac32e-9ef5-46fb-82a1-b76b840c0f7b'),
384+
).resolves.toMatchInlineSnapshot(`
385+
{
386+
"data": {
387+
"audience_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
388+
"created_at": "2024-12-01T19:32:22.980Z",
389+
"from": "Acme <[email protected]>",
390+
"id": "559ac32e-9ef5-46fb-82a1-b76b840c0f7b",
391+
"name": "Announcements",
392+
"object": "broadcast",
393+
"preview_text": "Check out our latest announcements",
394+
"reply_to": null,
395+
"scheduled_at": null,
396+
"sent_at": null,
397+
"status": "draft",
398+
"subject": "hello world",
399+
},
400+
"error": null,
401+
}
402+
`);
403+
});
404+
});
405+
406+
describe('remove', () => {
407+
it('removes a broadcast', async () => {
408+
const id = 'b01e0de9-7c27-4a53-bf38-2e3f98389a65';
409+
const response: RemoveBroadcastResponseSuccess = {
410+
object: 'broadcast',
411+
id,
412+
deleted: true,
413+
};
414+
fetchMock.mockOnce(JSON.stringify(response), {
415+
status: 200,
416+
headers: {
417+
'content-type': 'application/json',
418+
Authorization: 'Bearer re_924b3rjh2387fbewf823',
419+
},
420+
});
421+
422+
const resend = new Resend('re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop');
423+
424+
await expect(
425+
resend.broadcasts.remove(id),
426+
).resolves.toMatchInlineSnapshot(`
427+
{
428+
"data": {
429+
"deleted": true,
430+
"id": "b01e0de9-7c27-4a53-bf38-2e3f98389a65",
431+
"object": "broadcast",
432+
},
433+
"error": null,
434+
}
249435
`);
250436
});
251437
});

src/broadcasts/broadcasts.ts

+32
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ import type {
44
CreateBroadcastOptions,
55
CreateBroadcastRequestOptions,
66
} from './interfaces/create-broadcast-options.interface';
7+
import type {
8+
GetBroadcastResponse,
9+
GetBroadcastResponseSuccess,
10+
} from './interfaces/get-broadcast.interface';
11+
import type {
12+
ListBroadcastsResponse,
13+
ListBroadcastsResponseSuccess,
14+
} from './interfaces/list-broadcasts.interface';
15+
import type {
16+
RemoveBroadcastResponse,
17+
RemoveBroadcastResponseSuccess,
18+
} from './interfaces/remove-broadcast.interface';
719
import type {
820
SendBroadcastOptions,
921
SendBroadcastResponse,
@@ -64,4 +76,24 @@ export class Broadcasts {
6476

6577
return data;
6678
}
79+
80+
async list(): Promise<ListBroadcastsResponse> {
81+
const data =
82+
await this.resend.get<ListBroadcastsResponseSuccess>('/broadcasts');
83+
return data;
84+
}
85+
86+
async get(id: string): Promise<GetBroadcastResponse> {
87+
const data = await this.resend.get<GetBroadcastResponseSuccess>(
88+
`/broadcasts/${id}`,
89+
);
90+
return data;
91+
}
92+
93+
async remove(id: string): Promise<RemoveBroadcastResponse> {
94+
const data = await this.resend.delete<RemoveBroadcastResponseSuccess>(
95+
`/broadcasts/${id}`,
96+
);
97+
return data;
98+
}
6799
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export interface Broadcast {
2+
id: string;
3+
name: string;
4+
audience_id: string | null;
5+
from: string | null;
6+
subject: string | null;
7+
reply_to: string[] | null;
8+
preview_text: string | null;
9+
status: 'draft' | 'sent' | 'queued';
10+
created_at: string;
11+
scheduled_at: string | null;
12+
sent_at: string | null;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { ErrorResponse } from '../../interfaces';
2+
import type { Broadcast } from './broadcast';
3+
4+
export interface GetBroadcastResponseSuccess extends Broadcast {
5+
object: 'broadcast';
6+
}
7+
8+
export interface GetBroadcastResponse {
9+
data: GetBroadcastResponseSuccess | null;
10+
error: ErrorResponse | null;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { ErrorResponse } from '../../interfaces';
2+
import type { Broadcast } from './broadcast';
3+
4+
export type ListBroadcastsResponseSuccess = {
5+
object: 'list';
6+
data: Pick<
7+
Broadcast,
8+
| 'id'
9+
| 'name'
10+
| 'audience_id'
11+
| 'status'
12+
| 'created_at'
13+
| 'scheduled_at'
14+
| 'sent_at'
15+
>[];
16+
};
17+
18+
export interface ListBroadcastsResponse {
19+
data: ListBroadcastsResponseSuccess | null;
20+
error: ErrorResponse | null;
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { ErrorResponse } from '../../interfaces';
2+
import type { Broadcast } from './broadcast';
3+
4+
export interface RemoveBroadcastResponseSuccess extends Pick<Broadcast, 'id'> {
5+
object: 'broadcast';
6+
deleted: boolean;
7+
}
8+
9+
export interface RemoveBroadcastResponse {
10+
data: RemoveBroadcastResponseSuccess | null;
11+
error: ErrorResponse | null;
12+
}

src/emails/emails.spec.ts

+4
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ describe('Emails', () => {
371371
cc: null,
372372
reply_to: null,
373373
last_event: 'delivered',
374+
scheduled_at: null,
374375
};
375376

376377
fetchMock.mockOnce(JSON.stringify(response), {
@@ -395,6 +396,7 @@ describe('Emails', () => {
395396
"last_event": "delivered",
396397
"object": "email",
397398
"reply_to": null,
399+
"scheduled_at": null,
398400
"subject": "Test email",
399401
"text": null,
400402
"to": [
@@ -420,6 +422,7 @@ describe('Emails', () => {
420422
421423
reply_to: null,
422424
last_event: 'delivered',
425+
scheduled_at: null,
423426
};
424427

425428
fetchMock.mockOnce(JSON.stringify(response), {
@@ -447,6 +450,7 @@ describe('Emails', () => {
447450
"last_event": "delivered",
448451
"object": "email",
449452
"reply_to": null,
453+
"scheduled_at": null,
450454
"subject": "Test email",
451455
"text": null,
452456
"to": [

0 commit comments

Comments
 (0)