Skip to content

Commit 01c883e

Browse files
committed
feat: add inbound listing method (#668)
1 parent 79e7745 commit 01c883e

File tree

4 files changed

+264
-2
lines changed

4 files changed

+264
-2
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './get-inbound-email.interface';
2+
export * from './list-inbound-emails.interface';
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { PaginationOptions } from '../../../common/interfaces';
2+
import type { ErrorResponse } from '../../../interfaces';
3+
import type { GetInboundEmailResponseSuccess } from './get-inbound-email.interface';
4+
5+
export type ListInboundEmailsOptions = PaginationOptions;
6+
7+
export type ListInboundEmail = Omit<
8+
GetInboundEmailResponseSuccess,
9+
'html' | 'text' | 'headers' | 'object'
10+
>;
11+
12+
export interface ListInboundEmailsResponseSuccess {
13+
object: 'list';
14+
has_more: boolean;
15+
data: ListInboundEmail[];
16+
}
17+
18+
export type ListInboundEmailsResponse =
19+
| {
20+
data: ListInboundEmailsResponseSuccess;
21+
error: null;
22+
}
23+
| {
24+
data: null;
25+
error: ErrorResponse;
26+
};

src/emails/receiving/receiving.spec.ts

Lines changed: 218 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe('Receiving', () => {
1111
it('returns error', async () => {
1212
const response: ErrorResponse = {
1313
name: 'not_found',
14-
message: 'Inbound email not found',
14+
message: 'Email not found',
1515
};
1616

1717
fetchMock.mockOnce(JSON.stringify(response), {
@@ -30,7 +30,7 @@ describe('Receiving', () => {
3030
{
3131
"data": null,
3232
"error": {
33-
"message": "Inbound email not found",
33+
"message": "Email not found",
3434
"name": "not_found",
3535
},
3636
}
@@ -170,4 +170,220 @@ describe('Receiving', () => {
170170
});
171171
});
172172
});
173+
174+
describe('list', () => {
175+
describe('when no inbound emails found', () => {
176+
it('returns empty list', async () => {
177+
const response = {
178+
object: 'list' as const,
179+
has_more: false,
180+
data: [],
181+
};
182+
183+
fetchMock.mockOnce(JSON.stringify(response), {
184+
status: 200,
185+
headers: {
186+
'content-type': 'application/json',
187+
Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
188+
},
189+
});
190+
191+
const result = await resend.emails.receiving.list();
192+
193+
expect(result).toMatchInlineSnapshot(`
194+
{
195+
"data": {
196+
"data": [],
197+
"has_more": false,
198+
"object": "list",
199+
},
200+
"error": null,
201+
}
202+
`);
203+
});
204+
});
205+
206+
describe('when inbound emails found', () => {
207+
it('returns list of inbound emails with transformed fields', async () => {
208+
const apiResponse = {
209+
object: 'list' as const,
210+
has_more: true,
211+
data: [
212+
{
213+
id: '67d9bcdb-5a02-42d7-8da9-0d6feea18cff',
214+
215+
216+
created_at: '2023-04-07T23:13:52.669661+00:00',
217+
subject: 'Test inbound email 1',
218+
bcc: null,
219+
220+
reply_to: ['[email protected]'],
221+
attachments: [
222+
{
223+
id: 'att_123',
224+
filename: 'document.pdf',
225+
content_type: 'application/pdf',
226+
content_id: 'cid_123',
227+
content_disposition: 'attachment' as const,
228+
size: 12345,
229+
},
230+
],
231+
},
232+
{
233+
id: '87e9bcdb-6b03-43e8-9ea0-1e7gffa19d00',
234+
235+
236+
created_at: '2023-04-08T10:20:30.123456+00:00',
237+
subject: 'Test inbound email 2',
238+
239+
cc: null,
240+
reply_to: null,
241+
attachments: [],
242+
},
243+
],
244+
};
245+
246+
fetchMock.mockOnce(JSON.stringify(apiResponse), {
247+
status: 200,
248+
headers: {
249+
'content-type': 'application/json',
250+
Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
251+
},
252+
});
253+
254+
const result = await resend.emails.receiving.list();
255+
256+
expect(result).toMatchInlineSnapshot(`
257+
{
258+
"data": {
259+
"data": [
260+
{
261+
"attachments": [
262+
{
263+
"content_disposition": "attachment",
264+
"content_id": "cid_123",
265+
"content_type": "application/pdf",
266+
"filename": "document.pdf",
267+
"id": "att_123",
268+
"size": 12345,
269+
},
270+
],
271+
"bcc": null,
272+
"cc": [
273+
274+
],
275+
"created_at": "2023-04-07T23:13:52.669661+00:00",
276+
"from": "[email protected]",
277+
"id": "67d9bcdb-5a02-42d7-8da9-0d6feea18cff",
278+
"reply_to": [
279+
280+
],
281+
"subject": "Test inbound email 1",
282+
"to": [
283+
284+
],
285+
},
286+
{
287+
"attachments": [],
288+
"bcc": [
289+
290+
],
291+
"cc": null,
292+
"created_at": "2023-04-08T10:20:30.123456+00:00",
293+
"from": "[email protected]",
294+
"id": "87e9bcdb-6b03-43e8-9ea0-1e7gffa19d00",
295+
"reply_to": null,
296+
"subject": "Test inbound email 2",
297+
"to": [
298+
299+
],
300+
},
301+
],
302+
"has_more": true,
303+
"object": "list",
304+
},
305+
"error": null,
306+
}
307+
`);
308+
});
309+
310+
it('supports pagination with limit parameter', async () => {
311+
const apiResponse = {
312+
object: 'list' as const,
313+
has_more: true,
314+
data: [
315+
{
316+
id: '67d9bcdb-5a02-42d7-8da9-0d6feea18cff',
317+
318+
319+
created_at: '2023-04-07T23:13:52.669661+00:00',
320+
subject: 'Test inbound email',
321+
bcc: null,
322+
cc: null,
323+
reply_to: null,
324+
attachments: [],
325+
},
326+
],
327+
};
328+
329+
fetchMock.mockOnce(JSON.stringify(apiResponse), {
330+
status: 200,
331+
headers: {
332+
'content-type': 'application/json',
333+
Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
334+
},
335+
});
336+
337+
await resend.emails.receiving.list({ limit: 10 });
338+
339+
expect(fetchMock.mock.calls[0][0]).toBe(
340+
'https://api.resend.com/emails/receiving?limit=10',
341+
);
342+
});
343+
344+
it('supports pagination with after parameter', async () => {
345+
const apiResponse = {
346+
object: 'list' as const,
347+
has_more: false,
348+
data: [],
349+
};
350+
351+
fetchMock.mockOnce(JSON.stringify(apiResponse), {
352+
status: 200,
353+
headers: {
354+
'content-type': 'application/json',
355+
Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
356+
},
357+
});
358+
359+
await resend.emails.receiving.list({ after: 'cursor123' });
360+
361+
expect(fetchMock.mock.calls[0][0]).toBe(
362+
'https://api.resend.com/emails/receiving?after=cursor123',
363+
);
364+
});
365+
366+
it('supports pagination with before parameter', async () => {
367+
const apiResponse = {
368+
object: 'list' as const,
369+
has_more: false,
370+
data: [],
371+
};
372+
373+
fetchMock.mockOnce(JSON.stringify(apiResponse), {
374+
status: 200,
375+
headers: {
376+
'content-type': 'application/json',
377+
Authorization: 'Bearer re_zKa4RCko_Lhm9ost2YjNCctnPjbLw8Nop',
378+
},
379+
});
380+
381+
await resend.emails.receiving.list({ before: 'cursor456' });
382+
383+
expect(fetchMock.mock.calls[0][0]).toBe(
384+
'https://api.resend.com/emails/receiving?before=cursor456',
385+
);
386+
});
387+
});
388+
});
173389
});

src/emails/receiving/receiving.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
import { buildPaginationQuery } from '../../common/utils/build-pagination-query';
12
import type { Resend } from '../../resend';
23
import type {
34
GetInboundEmailResponse,
45
GetInboundEmailResponseSuccess,
56
} from './interfaces/get-inbound-email.interface';
7+
import type {
8+
ListInboundEmailsOptions,
9+
ListInboundEmailsResponse,
10+
ListInboundEmailsResponseSuccess,
11+
} from './interfaces/list-inbound-emails.interface';
612

713
export class Receiving {
814
constructor(private readonly resend: Resend) {}
@@ -14,4 +20,17 @@ export class Receiving {
1420

1521
return data;
1622
}
23+
24+
async list(
25+
options: ListInboundEmailsOptions = {},
26+
): Promise<ListInboundEmailsResponse> {
27+
const queryString = buildPaginationQuery(options);
28+
const url = queryString
29+
? `/emails/receiving?${queryString}`
30+
: '/emails/receiving';
31+
32+
const data = await this.resend.get<ListInboundEmailsResponseSuccess>(url);
33+
34+
return data;
35+
}
1736
}

0 commit comments

Comments
 (0)