Skip to content
Merged
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
11 changes: 11 additions & 0 deletions docs/channels/channel_management/deleting.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ channel.delete()
> If you recreate this channel, it will show up empty. Recovering old messages is not supported. Use the disable method if you want a reversible change.


### Keeping the messages

Pass `skip_truncate=True` to keep the messages of a soft deleted channel, so recreating the channel with the same id restores its history. It cannot be combined with a hard delete, and only distinct channels are eligible.

```python
channel.delete(skip_truncate=True)

# same option on the batch endpoint
response = client.delete_channels([cid1, cid2], skip_truncate=True)
```

## Deleting Many Channels

You can delete up to 100 channels and optionally all of their messages using this method. This can be a large amount of data to delete, so this endpoint processes asynchronously, meaning responses contain a `task ID` which can be polled using the [getTask endpoint](/chat/docs/python#tasks-gettask) to check status of the deletions. Channels will be soft-deleted immediately so that channels no longer return from queries, but permanently deleting the channel and deleting messages takes longer to process.
Expand Down
9 changes: 7 additions & 2 deletions stream_chat/async_chat/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,13 @@ async def update_partial(
payload = {"set": to_set or {}, "unset": to_unset or []}
return await self.client.patch(self.url, data=payload)

async def delete(self, hard: bool = False) -> StreamResponse:
return await self.client.delete(self.url, {"hard_delete": hard})
async def delete(
self, hard: bool = False, skip_truncate: bool = False
) -> StreamResponse:
params = {"hard_delete": hard}
if skip_truncate:
params["skip_truncate"] = True
return await self.client.delete(self.url, params)

async def truncate(self, **options: Any) -> StreamResponse:
return await self.client.post(f"{self.url}/truncate", data=options)
Expand Down
7 changes: 5 additions & 2 deletions stream_chat/base/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,14 @@ def update_partial(

@abc.abstractmethod
def delete(
self, hard: bool = False
self, hard: bool = False, skip_truncate: bool = False
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
"""
Delete the channel. Messages are permanently removed.
Delete the channel.

:param hard: hard delete the channel and its messages
:param skip_truncate: keep the messages of a soft deleted channel, so
recreating it with the same id restores the history
:return: The server response
"""
pass
Expand Down
7 changes: 5 additions & 2 deletions stream_chat/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,11 @@ def update_partial(
payload = {"set": to_set or {}, "unset": to_unset or []}
return self.client.patch(self.url, data=payload)

def delete(self, hard: bool = False) -> StreamResponse:
return self.client.delete(self.url, params={"hard_delete": hard})
def delete(self, hard: bool = False, skip_truncate: bool = False) -> StreamResponse:
params = {"hard_delete": hard}
if skip_truncate:
params["skip_truncate"] = True
return self.client.delete(self.url, params=params)

def truncate(self, **options: Any) -> StreamResponse:
return self.client.post(f"{self.url}/truncate", data=options)
Expand Down
42 changes: 42 additions & 0 deletions stream_chat/tests/test_channel_delete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from unittest.mock import AsyncMock, MagicMock

import pytest

from stream_chat.async_chat.channel import Channel as AsyncChannel
from stream_chat.channel import Channel


def test_delete_omits_skip_truncate_by_default() -> None:
client = MagicMock()
Channel(client, "messaging", "chan").delete()
client.delete.assert_called_once_with(
"channels/messaging/chan", params={"hard_delete": False}
)


def test_delete_sends_skip_truncate() -> None:
client = MagicMock()
Channel(client, "messaging", "chan").delete(skip_truncate=True)
client.delete.assert_called_once_with(
"channels/messaging/chan", params={"hard_delete": False, "skip_truncate": True}
)


@pytest.mark.asyncio
async def test_async_delete_omits_skip_truncate_by_default() -> None:
client = MagicMock()
client.delete = AsyncMock()
await AsyncChannel(client, "messaging", "chan").delete()
client.delete.assert_called_once_with(
"channels/messaging/chan", {"hard_delete": False}
)


@pytest.mark.asyncio
async def test_async_delete_sends_skip_truncate() -> None:
client = MagicMock()
client.delete = AsyncMock()
await AsyncChannel(client, "messaging", "chan").delete(skip_truncate=True)
client.delete.assert_called_once_with(
"channels/messaging/chan", {"hard_delete": False, "skip_truncate": True}
)
Loading