Skip to content

Commit 631a4ad

Browse files
authored
feat(contacts): add segment_id support to Contacts.list (#202)
1 parent 3854817 commit 631a4ad

3 files changed

Lines changed: 116 additions & 10 deletions

File tree

resend/contacts/_contacts.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -250,29 +250,34 @@ def update(cls, params: UpdateParams) -> UpdateContactResponse:
250250

251251
@classmethod
252252
def list(
253-
cls, audience_id: Optional[str] = None, params: Optional[ListParams] = None
253+
cls,
254+
audience_id: Optional[str] = None,
255+
params: Optional[ListParams] = None,
256+
segment_id: Optional[str] = None,
254257
) -> ListResponse:
255258
"""
256259
List all contacts.
257-
Can list either global contacts or audience-specific contacts.
260+
Can list global contacts, audience-specific contacts, or segment contacts.
258261
see more: https://resend.com/docs/api-reference/contacts/list-contacts
259262
260263
Args:
261-
audience_id (Optional[str]): The audience ID. If not provided, lists all global contacts.
264+
audience_id (Optional[str]): Deprecated. Use segment_id instead.
262265
params (Optional[ListParams]): Optional pagination parameters
263266
- limit: Number of contacts to retrieve (max 100, min 1).
264267
If not provided, all contacts will be returned without pagination.
265268
- after: ID after which to retrieve more contacts
266269
- before: ID before which to retrieve more contacts
270+
segment_id (Optional[str]): The segment ID. When provided, lists contacts
271+
in the segment via GET /segments/{segment_id}/contacts.
267272
268273
Returns:
269274
ListResponse: A list of contact objects
270275
"""
271-
if audience_id:
272-
# Audience-specific contacts
276+
if segment_id:
277+
base_path = f"/segments/{segment_id}/contacts"
278+
elif audience_id:
273279
base_path = f"/audiences/{audience_id}/contacts"
274280
else:
275-
# Global contacts
276281
base_path = "/contacts"
277282

278283
query_params = cast(Dict[Any, Any], params) if params else None
@@ -419,21 +424,28 @@ async def update_async(cls, params: UpdateParams) -> UpdateContactResponse:
419424

420425
@classmethod
421426
async def list_async(
422-
cls, audience_id: Optional[str] = None, params: Optional[ListParams] = None
427+
cls,
428+
audience_id: Optional[str] = None,
429+
params: Optional[ListParams] = None,
430+
segment_id: Optional[str] = None,
423431
) -> ListResponse:
424432
"""
425433
List all contacts (async).
426-
Can list either global contacts or audience-specific contacts.
434+
Can list global contacts, audience-specific contacts, or segment contacts.
427435
see more: https://resend.com/docs/api-reference/contacts/list-contacts
428436
429437
Args:
430-
audience_id (Optional[str]): The audience ID. If not provided, lists all global contacts.
438+
audience_id (Optional[str]): Deprecated. Use segment_id instead.
431439
params (Optional[ListParams]): Optional pagination parameters
440+
segment_id (Optional[str]): The segment ID. When provided, lists contacts
441+
in the segment via GET /segments/{segment_id}/contacts.
432442
433443
Returns:
434444
ListResponse: A list of contact objects
435445
"""
436-
if audience_id:
446+
if segment_id:
447+
base_path = f"/segments/{segment_id}/contacts"
448+
elif audience_id:
437449
base_path = f"/audiences/{audience_id}/contacts"
438450
else:
439451
base_path = "/contacts"

tests/contacts_async_test.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,38 @@ async def test_should_list_contacts_async_raise_exception_when_no_content(
248248
_ = await resend.Contacts.list_async(
249249
audience_id="48c269ed-9873-4d60-bdd9-cd7e6fc0b9b8"
250250
)
251+
252+
async def test_contacts_list_async_by_segment_id(self) -> None:
253+
self.set_mock_json(
254+
{
255+
"object": "list",
256+
"has_more": False,
257+
"data": [
258+
{
259+
"id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
260+
"email": "steve.wozniak@gmail.com",
261+
"first_name": "Steve",
262+
"last_name": "Wozniak",
263+
"created_at": "2023-10-06T23:47:56.678Z",
264+
"unsubscribed": False,
265+
}
266+
],
267+
}
268+
)
269+
270+
contacts: resend.Contacts.ListResponse = await resend.Contacts.list_async(
271+
segment_id="78261eea-8f8b-4381-83c6-79fa7120f1cf"
272+
)
273+
assert contacts["object"] == "list"
274+
assert contacts["has_more"] is False
275+
assert contacts["data"][0]["id"] == "e169aa45-1ecf-4183-9955-b1499d5701d3"
276+
assert contacts["data"][0]["email"] == "steve.wozniak@gmail.com"
277+
278+
async def test_should_list_contacts_async_by_segment_raise_exception_when_no_content(
279+
self,
280+
) -> None:
281+
self.set_mock_json(None)
282+
with pytest.raises(NoContentError):
283+
_ = await resend.Contacts.list_async(
284+
segment_id="78261eea-8f8b-4381-83c6-79fa7120f1cf"
285+
)

tests/contacts_test.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,65 @@ def test_contacts_list_global(self) -> None:
387387
assert contacts["data"][0]["id"] == "global-1"
388388
assert contacts["data"][1]["id"] == "global-2"
389389

390+
def test_contacts_list_by_segment_id(self) -> None:
391+
self.set_mock_json(
392+
{
393+
"object": "list",
394+
"has_more": False,
395+
"data": [
396+
{
397+
"id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
398+
"email": "steve.wozniak@gmail.com",
399+
"first_name": "Steve",
400+
"last_name": "Wozniak",
401+
"created_at": "2023-10-06T23:47:56.678Z",
402+
"unsubscribed": False,
403+
}
404+
],
405+
}
406+
)
407+
408+
contacts: resend.Contacts.ListResponse = resend.Contacts.list(
409+
segment_id="78261eea-8f8b-4381-83c6-79fa7120f1cf"
410+
)
411+
assert contacts["object"] == "list"
412+
assert contacts["has_more"] is False
413+
assert contacts["data"][0]["id"] == "e169aa45-1ecf-4183-9955-b1499d5701d3"
414+
assert contacts["data"][0]["email"] == "steve.wozniak@gmail.com"
415+
416+
def test_contacts_list_by_segment_id_with_pagination(self) -> None:
417+
self.set_mock_json(
418+
{
419+
"object": "list",
420+
"has_more": True,
421+
"data": [
422+
{
423+
"id": "contact-1",
424+
"email": "contact1@example.com",
425+
"first_name": "Contact",
426+
"last_name": "One",
427+
"created_at": "2023-10-06T23:47:56.678Z",
428+
"unsubscribed": False,
429+
}
430+
],
431+
}
432+
)
433+
434+
params: resend.Contacts.ListParams = {"limit": 10, "after": "previous-id"}
435+
contacts: resend.Contacts.ListResponse = resend.Contacts.list(
436+
segment_id="78261eea-8f8b-4381-83c6-79fa7120f1cf", params=params
437+
)
438+
assert contacts["object"] == "list"
439+
assert contacts["has_more"] is True
440+
assert len(contacts["data"]) == 1
441+
442+
def test_should_list_contacts_by_segment_raise_exception_when_no_content(
443+
self,
444+
) -> None:
445+
self.set_mock_json(None)
446+
with self.assertRaises(NoContentError):
447+
_ = resend.Contacts.list(segment_id="78261eea-8f8b-4381-83c6-79fa7120f1cf")
448+
390449
def test_contacts_remove_global_by_id(self) -> None:
391450
self.set_mock_json(
392451
{

0 commit comments

Comments
 (0)