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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ secrets.*sh
.idea

.venv
venv
.python-version
pip-selfcheck.json
.idea
Expand Down
7 changes: 7 additions & 0 deletions stream_chat/async_chat/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,13 @@ async def query_message_history(
)
return await self.post("messages/history", data=params)

async def query_threads(
self, filter: Dict = None, sort: List[Dict] = None, **options: Any
) -> StreamResponse:
params = options.copy()
params.update({"filter": filter, "sort": self.normalize_sort(sort)})
return await self.post("threads", data=params)

async def query_users(
self, filter_conditions: Dict, sort: List[Dict] = None, **options: Any
) -> StreamResponse:
Expand Down
13 changes: 13 additions & 0 deletions stream_chat/base/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,19 @@ def query_message_history(
"""
pass

@abc.abstractmethod
def query_threads(
self, filter: Dict = None, sort: List[Dict] = None, **options: Any
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
"""
Allows you to query threads using filter and sort. You can find the complete list of supported operators in the query syntax section of the docs.

:param filter: Filter conditions for the query
:param sort: Sort conditions for the query
:return: StreamResponse containing the threads
"""
pass

@abc.abstractmethod
def query_users(
self, filter_conditions: Dict, sort: List[Dict] = None, **options: Any
Expand Down
31 changes: 31 additions & 0 deletions stream_chat/base/query_threads.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import abc
from typing import Any, Awaitable, Dict, List, Union

from stream_chat.base.client import StreamChatInterface
from stream_chat.types.stream_response import StreamResponse


class QueryThreadsInterface(abc.ABC):
@abc.abstractmethod
def __init__(self, client: StreamChatInterface):
self.client = client

@property
def url(self) -> str:
return "threads"

@abc.abstractmethod
def query_threads(
self,
filter: Dict[str, Dict[str, Any]],
sort: List[Dict[str, Any]],
**options: Any,
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
"""
Get a list of threads given filter and sort options

:param filter: filter conditions (e.g. {"created_by_user_id": {"$eq": "user_123"}})
:param sort: sort options (e.g. [{"field": "last_message_at", "direction": -1}])
:return: the Server Response
"""
pass
7 changes: 7 additions & 0 deletions stream_chat/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,13 @@ def query_message_history(
params.update({"filter": filter, "sort": self.normalize_sort(sort)})
return self.post("messages/history", data=params)

def query_threads(
self, filter: Dict = None, sort: List[Dict] = None, **options: Any
) -> StreamResponse:
params = options.copy()
params.update({"filter": filter, "sort": self.normalize_sort(sort)})
return self.post("threads", data=params)

def query_users(
self, filter_conditions: Dict, sort: List[Dict] = None, **options: Any
) -> StreamResponse:
Expand Down
15 changes: 15 additions & 0 deletions stream_chat/query_threads.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from typing import Any, Awaitable, Dict, List, Union

from stream_chat.base.query_threads import QueryThreadsInterface
from stream_chat.types.stream_response import StreamResponse


class QueryThreads(QueryThreadsInterface):
def query_threads(
self,
filter: Dict[str, Dict[str, Any]],
sort: List[Dict[str, Any]],
**options: Any,
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
payload = {"filter": filter, "sort": sort, **options}
return self.client.post(self.url, data=payload)
75 changes: 75 additions & 0 deletions stream_chat/tests/async_chat/test_query_threads.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from typing import Dict

import pytest

from stream_chat.async_chat import StreamChatAsync
from stream_chat.types.stream_response import StreamResponse


@pytest.mark.incremental
class TestQueryThreads:
@pytest.mark.asyncio
async def test_query_threads(
self, client: StreamChatAsync, channel, random_user: Dict
):
# Create a thread with some messages
parent_message = await channel.send_message(
{"text": "Parent message"}, random_user["id"]
)
thread_message = await channel.send_message(
{"text": "Thread message", "parent_id": parent_message["message"]["id"]},
random_user["id"],
)

# Query threads with filter and sort
filter_conditions = {"parent_id": parent_message["message"]["id"]}
sort_conditions = [{"field": "created_at", "direction": -1}]

response = await client.query_threads(
filter=filter_conditions, sort=sort_conditions, user_id=random_user["id"]
)

assert isinstance(response, StreamResponse)
assert "threads" in response
assert len(response["threads"]) > 0

# Verify the thread message is in the response
thread = response["threads"][0]
assert "latest_replies" in thread
assert len(thread["latest_replies"]) > 0
assert thread["latest_replies"][0]["text"] == thread_message["message"]["text"]

@pytest.mark.asyncio
async def test_query_threads_with_options(
self, client: StreamChatAsync, channel, random_user: Dict
):
# Create a thread with multiple messages
parent_message = await channel.send_message(
{"text": "Parent message"}, random_user["id"]
)
thread_messages = []
for i in range(3):
msg = await channel.send_message(
{
"text": f"Thread message {i}",
"parent_id": parent_message["message"]["id"],
},
random_user["id"],
)
thread_messages.append(msg)

# Query threads with limit and offset
filter_conditions = {"parent_id": parent_message["message"]["id"]}
sort_conditions = [{"field": "created_at", "direction": -1}]

response = await client.query_threads(
filter=filter_conditions,
sort=sort_conditions,
limit=1,
user_id=random_user["id"],
)

assert isinstance(response, StreamResponse)
assert "threads" in response
assert len(response["threads"]) == 1
assert "next" in response
66 changes: 66 additions & 0 deletions stream_chat/tests/test_query_threads.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from typing import Dict

import pytest

from stream_chat import StreamChat
from stream_chat.types.stream_response import StreamResponse


@pytest.mark.incremental
class TestQueryThreads:
def test_query_threads(self, client: StreamChat, channel, random_user: Dict):
parent_message = channel.send_message(
{"text": "Parent message"}, random_user["id"]
)
thread_message = channel.send_message(
{"text": "Thread message", "parent_id": parent_message["message"]["id"]},
random_user["id"],
)

filter_conditions = {"parent_id": parent_message["message"]["id"]}
sort_conditions = [{"field": "created_at", "direction": -1}]

response = client.query_threads(
filter=filter_conditions, sort=sort_conditions, user_id=random_user["id"]
)

assert isinstance(response, StreamResponse)
assert "threads" in response
assert len(response["threads"]) > 0

thread = response["threads"][0]
assert "latest_replies" in thread
assert len(thread["latest_replies"]) > 0
assert thread["latest_replies"][0]["text"] == thread_message["message"]["text"]

def test_query_threads_with_options(
self, client: StreamChat, channel, random_user: Dict
):
parent_message = channel.send_message(
{"text": "Parent message"}, random_user["id"]
)
thread_messages = []
for i in range(3):
msg = channel.send_message(
{
"text": f"Thread message {i}",
"parent_id": parent_message["message"]["id"],
},
random_user["id"],
)
thread_messages.append(msg)

filter_conditions = {"parent_id": parent_message["message"]["id"]}
sort_conditions = [{"field": "created_at", "direction": -1}]

response = client.query_threads(
filter=filter_conditions,
sort=sort_conditions,
limit=1,
user_id=random_user["id"],
)

assert isinstance(response, StreamResponse)
assert "threads" in response
assert len(response["threads"]) == 1
assert "next" in response