Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions resend/broadcasts/_broadcasts.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,24 @@ class ListResponse(BaseResponse):
Whether there are more results available for pagination
"""

class CancelResponse(BaseResponse):
"""
CancelResponse is the class that wraps the response of the cancel method.

Attributes:
object (str): object type: "broadcast"
id (str): id of the canceled broadcast
"""

object: str
"""
object type: "broadcast"
"""
id: str
"""
id of the canceled broadcast
"""

class RemoveResponse(BaseResponse):
"""
RemoveResponse is the class that wraps the response of the remove method.
Expand Down Expand Up @@ -360,6 +378,24 @@ def get(cls, id: str) -> Broadcast:
).perform_with_content()
return resp

@classmethod
def cancel(cls, id: str) -> CancelResponse:
"""
Cancel a queued or scheduled broadcast.
see more: https://resend.com/docs/api-reference/broadcasts/cancel-broadcast

Args:
id (str): The broadcast ID

Returns:
CancelResponse: The cancel response object
"""
path = f"/broadcasts/{id}/cancel"
resp = request.Request[Broadcasts.CancelResponse](
path=path, params={}, verb="post"
).perform_with_content()
return resp

@classmethod
def remove(cls, id: str) -> RemoveResponse:
"""
Expand Down Expand Up @@ -470,6 +506,24 @@ async def get_async(cls, id: str) -> Broadcast:
).perform_with_content()
return resp

@classmethod
async def cancel_async(cls, id: str) -> CancelResponse:
"""
Cancel a queued or scheduled broadcast (async).
see more: https://resend.com/docs/api-reference/broadcasts/cancel-broadcast

Args:
id (str): The broadcast ID

Returns:
CancelResponse: The cancel response object
"""
path = f"/broadcasts/{id}/cancel"
resp = await AsyncRequest[Broadcasts.CancelResponse](
path=path, params={}, verb="post"
).perform_with_content()
return resp

@classmethod
async def remove_async(cls, id: str) -> RemoveResponse:
"""
Expand Down
14 changes: 14 additions & 0 deletions tests/broadcasts_async_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,20 @@ async def test_should_send_broadcasts_async_raise_exception_when_no_content(
with pytest.raises(NoContentError):
_ = await resend.Broadcasts.send_async(params)

async def test_broadcasts_cancel_async(self) -> None:
self.set_mock_json(
{
"object": "broadcast",
"id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
}
)

canceled = await resend.Broadcasts.cancel_async(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The cancel_async coverage here only exercises the success path. Every other method in this file has a paired raise-exception-when-no-content test; add one for cancel_async (set_mock_json(None), expect NoContentError) to keep the error path covered and consistent with the rest of the suite.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/broadcasts_async_test.py, line 136:

<comment>The cancel_async coverage here only exercises the success path. Every other method in this file has a paired raise-exception-when-no-content test; add one for cancel_async (set_mock_json(None), expect NoContentError) to keep the error path covered and consistent with the rest of the suite.</comment>

<file context>
@@ -125,6 +125,20 @@ async def test_should_send_broadcasts_async_raise_exception_when_no_content(
+            }
+        )
+
+        canceled = await resend.Broadcasts.cancel_async(
+            "78261eea-8f8b-4381-83c6-79fa7120f1cf"
+        )
</file context>

"78261eea-8f8b-4381-83c6-79fa7120f1cf"
)
assert canceled["id"] == "78261eea-8f8b-4381-83c6-79fa7120f1cf"
assert canceled["object"] == "broadcast"

async def test_broadcasts_remove_async(self) -> None:
self.set_mock_json(
{
Expand Down
12 changes: 12 additions & 0 deletions tests/broadcasts_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,18 @@ def test_broadcasts_create_and_schedule(self) -> None:
broadcast: resend.Broadcasts.CreateResponse = resend.Broadcasts.create(params)
assert broadcast["id"] == "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794"

def test_broadcasts_cancel(self) -> None:
self.set_mock_json(
{
"object": "broadcast",
"id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
}
)

canceled = resend.Broadcasts.cancel("78261eea-8f8b-4381-83c6-79fa7120f1cf")
assert canceled["id"] == "78261eea-8f8b-4381-83c6-79fa7120f1cf"
assert canceled["object"] == "broadcast"

def test_broadcasts_remove(self) -> None:
self.set_mock_json(
{
Expand Down
Loading