Skip to content

Commit c2b42c5

Browse files
adityaalifntotalimmersiongithub-actions[bot]
authored
[CHA-794] Add query threads (#198)
* Add query threads * Fix lint * Fix lint * Fix lint * Fix test * Update stream_chat/tests/async_chat/test_query_threads.py Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --------- Co-authored-by: Lennart <[email protected]> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 564e947 commit c2b42c5

File tree

8 files changed

+215
-0
lines changed

8 files changed

+215
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ secrets.*sh
6060
.idea
6161

6262
.venv
63+
venv
6364
.python-version
6465
pip-selfcheck.json
6566
.idea

stream_chat/async_chat/client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,13 @@ async def query_message_history(
360360
)
361361
return await self.post("messages/history", data=params)
362362

363+
async def query_threads(
364+
self, filter: Dict = None, sort: List[Dict] = None, **options: Any
365+
) -> StreamResponse:
366+
params = options.copy()
367+
params.update({"filter": filter, "sort": self.normalize_sort(sort)})
368+
return await self.post("threads", data=params)
369+
363370
async def query_users(
364371
self, filter_conditions: Dict, sort: List[Dict] = None, **options: Any
365372
) -> StreamResponse:

stream_chat/base/client.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,19 @@ def query_message_history(
562562
"""
563563
pass
564564

565+
@abc.abstractmethod
566+
def query_threads(
567+
self, filter: Dict = None, sort: List[Dict] = None, **options: Any
568+
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
569+
"""
570+
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.
571+
572+
:param filter: Filter conditions for the query
573+
:param sort: Sort conditions for the query
574+
:return: StreamResponse containing the threads
575+
"""
576+
pass
577+
565578
@abc.abstractmethod
566579
def query_users(
567580
self, filter_conditions: Dict, sort: List[Dict] = None, **options: Any

stream_chat/base/query_threads.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import abc
2+
from typing import Any, Awaitable, Dict, List, Union
3+
4+
from stream_chat.base.client import StreamChatInterface
5+
from stream_chat.types.stream_response import StreamResponse
6+
7+
8+
class QueryThreadsInterface(abc.ABC):
9+
@abc.abstractmethod
10+
def __init__(self, client: StreamChatInterface):
11+
self.client = client
12+
13+
@property
14+
def url(self) -> str:
15+
return "threads"
16+
17+
@abc.abstractmethod
18+
def query_threads(
19+
self,
20+
filter: Dict[str, Dict[str, Any]],
21+
sort: List[Dict[str, Any]],
22+
**options: Any,
23+
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
24+
"""
25+
Get a list of threads given filter and sort options
26+
27+
:param filter: filter conditions (e.g. {"created_by_user_id": {"$eq": "user_123"}})
28+
:param sort: sort options (e.g. [{"field": "last_message_at", "direction": -1}])
29+
:return: the Server Response
30+
"""
31+
pass

stream_chat/client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,13 @@ def query_message_history(
342342
params.update({"filter": filter, "sort": self.normalize_sort(sort)})
343343
return self.post("messages/history", data=params)
344344

345+
def query_threads(
346+
self, filter: Dict = None, sort: List[Dict] = None, **options: Any
347+
) -> StreamResponse:
348+
params = options.copy()
349+
params.update({"filter": filter, "sort": self.normalize_sort(sort)})
350+
return self.post("threads", data=params)
351+
345352
def query_users(
346353
self, filter_conditions: Dict, sort: List[Dict] = None, **options: Any
347354
) -> StreamResponse:

stream_chat/query_threads.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from typing import Any, Awaitable, Dict, List, Union
2+
3+
from stream_chat.base.query_threads import QueryThreadsInterface
4+
from stream_chat.types.stream_response import StreamResponse
5+
6+
7+
class QueryThreads(QueryThreadsInterface):
8+
def query_threads(
9+
self,
10+
filter: Dict[str, Dict[str, Any]],
11+
sort: List[Dict[str, Any]],
12+
**options: Any,
13+
) -> Union[StreamResponse, Awaitable[StreamResponse]]:
14+
payload = {"filter": filter, "sort": sort, **options}
15+
return self.client.post(self.url, data=payload)
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from typing import Dict
2+
3+
import pytest
4+
5+
from stream_chat.async_chat import StreamChatAsync
6+
from stream_chat.types.stream_response import StreamResponse
7+
8+
9+
@pytest.mark.incremental
10+
class TestQueryThreads:
11+
@pytest.mark.asyncio
12+
async def test_query_threads(
13+
self, client: StreamChatAsync, channel, random_user: Dict
14+
):
15+
# Create a thread with some messages
16+
parent_message = await channel.send_message(
17+
{"text": "Parent message"}, random_user["id"]
18+
)
19+
thread_message = await channel.send_message(
20+
{"text": "Thread message", "parent_id": parent_message["message"]["id"]},
21+
random_user["id"],
22+
)
23+
24+
# Query threads with filter and sort
25+
filter_conditions = {"parent_id": parent_message["message"]["id"]}
26+
sort_conditions = [{"field": "created_at", "direction": -1}]
27+
28+
response = await client.query_threads(
29+
filter=filter_conditions, sort=sort_conditions, user_id=random_user["id"]
30+
)
31+
32+
assert isinstance(response, StreamResponse)
33+
assert "threads" in response
34+
assert len(response["threads"]) > 0
35+
36+
# Verify the thread message is in the response
37+
thread = response["threads"][0]
38+
assert "latest_replies" in thread
39+
assert len(thread["latest_replies"]) > 0
40+
assert thread["latest_replies"][0]["text"] == thread_message["message"]["text"]
41+
42+
@pytest.mark.asyncio
43+
async def test_query_threads_with_options(
44+
self, client: StreamChatAsync, channel, random_user: Dict
45+
):
46+
# Create a thread with multiple messages
47+
parent_message = await channel.send_message(
48+
{"text": "Parent message"}, random_user["id"]
49+
)
50+
thread_messages = []
51+
for i in range(3):
52+
msg = await channel.send_message(
53+
{
54+
"text": f"Thread message {i}",
55+
"parent_id": parent_message["message"]["id"],
56+
},
57+
random_user["id"],
58+
)
59+
thread_messages.append(msg)
60+
61+
# Query threads with limit and offset
62+
filter_conditions = {"parent_id": parent_message["message"]["id"]}
63+
sort_conditions = [{"field": "created_at", "direction": -1}]
64+
65+
response = await client.query_threads(
66+
filter=filter_conditions,
67+
sort=sort_conditions,
68+
limit=1,
69+
user_id=random_user["id"],
70+
)
71+
72+
assert isinstance(response, StreamResponse)
73+
assert "threads" in response
74+
assert len(response["threads"]) == 1
75+
assert "next" in response
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
from typing import Dict
2+
3+
import pytest
4+
5+
from stream_chat import StreamChat
6+
from stream_chat.types.stream_response import StreamResponse
7+
8+
9+
@pytest.mark.incremental
10+
class TestQueryThreads:
11+
def test_query_threads(self, client: StreamChat, channel, random_user: Dict):
12+
parent_message = channel.send_message(
13+
{"text": "Parent message"}, random_user["id"]
14+
)
15+
thread_message = channel.send_message(
16+
{"text": "Thread message", "parent_id": parent_message["message"]["id"]},
17+
random_user["id"],
18+
)
19+
20+
filter_conditions = {"parent_id": parent_message["message"]["id"]}
21+
sort_conditions = [{"field": "created_at", "direction": -1}]
22+
23+
response = client.query_threads(
24+
filter=filter_conditions, sort=sort_conditions, user_id=random_user["id"]
25+
)
26+
27+
assert isinstance(response, StreamResponse)
28+
assert "threads" in response
29+
assert len(response["threads"]) > 0
30+
31+
thread = response["threads"][0]
32+
assert "latest_replies" in thread
33+
assert len(thread["latest_replies"]) > 0
34+
assert thread["latest_replies"][0]["text"] == thread_message["message"]["text"]
35+
36+
def test_query_threads_with_options(
37+
self, client: StreamChat, channel, random_user: Dict
38+
):
39+
parent_message = channel.send_message(
40+
{"text": "Parent message"}, random_user["id"]
41+
)
42+
thread_messages = []
43+
for i in range(3):
44+
msg = channel.send_message(
45+
{
46+
"text": f"Thread message {i}",
47+
"parent_id": parent_message["message"]["id"],
48+
},
49+
random_user["id"],
50+
)
51+
thread_messages.append(msg)
52+
53+
filter_conditions = {"parent_id": parent_message["message"]["id"]}
54+
sort_conditions = [{"field": "created_at", "direction": -1}]
55+
56+
response = client.query_threads(
57+
filter=filter_conditions,
58+
sort=sort_conditions,
59+
limit=1,
60+
user_id=random_user["id"],
61+
)
62+
63+
assert isinstance(response, StreamResponse)
64+
assert "threads" in response
65+
assert len(response["threads"]) == 1
66+
assert "next" in response

0 commit comments

Comments
 (0)