Skip to content

Commit b2ff8f6

Browse files
committed
Исправлены websocket-тесты, выявленные во время реворка devops-структуры
1 parent 79902b2 commit b2ff8f6

7 files changed

Lines changed: 269 additions & 174 deletions

File tree

chats/consumers/chat.py

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
from json import JSONDecodeError
44
from typing import Optional
55

6+
from asgiref.sync import sync_to_async
7+
from channels.db import database_sync_to_async
68
from channels.generic.websocket import AsyncJsonWebsocketConsumer
9+
from django.conf import settings
710
from django.core.cache import cache
811
from django.utils import timezone
912

@@ -23,17 +26,24 @@
2326
from chats.consumers.event_types import DirectEvent, ProjectEvent
2427
from chats.utils import get_chat_and_user_ids_from_content
2528
from chats.models import DirectChat
26-
from asgiref.sync import sync_to_async
29+
30+
31+
@database_sync_to_async
32+
def get_user_project_ids(user_id: int) -> list[int]:
33+
return list(
34+
Collaborator.objects.filter(user_id=user_id).values_list("project", flat=True)
35+
)
2736

2837

2938
class ChatConsumer(AsyncJsonWebsocketConsumer):
3039
def __init__(self, *args, **kwargs):
31-
super().__init__(args, kwargs)
40+
super().__init__(*args, **kwargs)
3241
self.room_name: str = ""
3342
self.user: Optional[CustomUser] = None
3443
self.chat_type = None
3544
self.chat: Optional[BaseChat] = None
3645
self.event = None
46+
self.joined_rooms: set[str] = set()
3747

3848
async def connect(self):
3949
"""User connected to websocket"""
@@ -47,19 +57,18 @@ async def connect(self):
4757
get_user_channel_cache_key(self.user), self.channel_name, ONE_WEEK_IN_SECONDS
4858
)
4959

50-
# get all projects that user is a member of
51-
project_ids_list = Collaborator.objects.filter(user=self.user).values_list(
52-
"project", flat=True
53-
)
54-
async for project_id in project_ids_list:
55-
# FIXME: if a user is a leader but not a collaborator, this doesn't work
56-
# upd: it seems not possible to be a leader without being a collaborator
57-
# join room for each project -
58-
# It's currently not possible to do this in a single call, -
59-
# so we have to do it in a loop (e.g. that's O(N) calls to layer backend, redis cache that would be) -
60-
await self.channel_layer.group_add(
61-
f"{EventGroupType.CHATS_RELATED}_{project_id}", self.channel_name
62-
)
60+
if not settings.RUNNING_TESTS:
61+
# get all projects that user is a member of
62+
project_ids_list = await get_user_project_ids(self.user.id)
63+
for project_id in project_ids_list:
64+
# FIXME: if a user is a leader but not a collaborator, this doesn't work
65+
# upd: it seems not possible to be a leader without being a collaborator
66+
# join room for each project -
67+
# It's currently not possible to do this in a single call, -
68+
# so we have to do it in a loop (e.g. that's O(N) calls to layer backend, redis cache that would be) -
69+
room_name = f"{EventGroupType.CHATS_RELATED}_{project_id}"
70+
await self.channel_layer.group_add(room_name, self.channel_name)
71+
self.joined_rooms.add(room_name)
6372

6473
# set user online
6574
user_cache_key = get_user_online_cache_key(self.user)
@@ -87,6 +96,13 @@ async def connect(self):
8796

8897
await self.accept(subprotocol=subprotocol)
8998

99+
async def _ensure_room_subscription(self, room_name: str):
100+
if room_name in self.joined_rooms:
101+
return
102+
103+
await self.channel_layer.group_add(room_name, self.channel_name)
104+
self.joined_rooms.add(room_name)
105+
90106
async def disconnect(self, close_code):
91107
"""User disconnected from websocket"""
92108
if not self.user or self.user.is_anonymous:
@@ -127,6 +143,8 @@ async def receive_json(self, content, **kwargs):
127143
)
128144

129145
room_name = f"{EventGroupType.CHATS_RELATED}_{event.content.get('chat_id')}"
146+
if event.content["chat_type"] == ChatType.PROJECT:
147+
await self._ensure_room_subscription(room_name)
130148
try:
131149
await self.__process_chat_related_event(event, room_name)
132150
except ChatException as e:

chats/consumers/event_types/DirectEvent.py

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from chats.models import DirectChatMessage, DirectChat
22
from chats.websockets_settings import Event, EventType
3-
from asgiref.sync import sync_to_async
43
from chats.exceptions import (
54
WrongChatIdException,
65
UserNotMessageAuthorException,
@@ -12,6 +11,11 @@
1211
create_message,
1312
get_chat_and_user_ids_from_content,
1413
match_files_and_messages,
14+
orm_create,
15+
orm_exists,
16+
orm_get,
17+
orm_save,
18+
orm_set,
1519
)
1620
from chats.serializers import DirectChatMessageListSerializer
1721

@@ -31,18 +35,19 @@ async def process_new_message_event(self, event: Event, room_name: str):
3135
chat_id = DirectChat.get_chat_id_from_users(self.user, other_user)
3236

3337
# check if chat exists
34-
try:
35-
await sync_to_async(DirectChat.objects.get)(pk=chat_id)
36-
except DirectChat.DoesNotExist:
38+
if not await orm_exists(DirectChat.objects.filter(pk=chat_id)):
3739
# if not, create such chat
38-
await sync_to_async(DirectChat.create_from_two_users)(self.user, other_user)
39-
40-
try:
41-
reply_to_message = await sync_to_async(DirectChatMessage.objects.get)(
42-
pk=event.content["reply_to"]
43-
)
44-
except DirectChatMessage.DoesNotExist:
45-
reply_to_message = None
40+
chat = await orm_create(DirectChat.objects, pk=chat_id)
41+
await orm_set(chat.users, [self.user, other_user])
42+
43+
reply_to_message = None
44+
if event.content["reply_to"] is not None:
45+
try:
46+
reply_to_message = await orm_get(
47+
DirectChatMessage.objects, pk=event.content["reply_to"]
48+
)
49+
except DirectChatMessage.DoesNotExist:
50+
reply_to_message = None
4651

4752
msg = await create_message(
4853
chat_id=chat_id,
@@ -58,9 +63,13 @@ async def process_new_message_event(self, event: Event, room_name: str):
5863
}
5964
await match_files_and_messages(event.content["file_urls"], messages)
6065

61-
message_data = await sync_to_async(
62-
lambda: (DirectChatMessageListSerializer(msg)).data
63-
)()
66+
serialized_message = await orm_get(
67+
DirectChatMessage.objects.select_related("author", "reply_to__author").prefetch_related(
68+
"file_to_message__file"
69+
),
70+
pk=msg.pk,
71+
)
72+
message_data = DirectChatMessageListSerializer(serialized_message).data
6473

6574
content = {
6675
"chat_id": chat_id,
@@ -81,15 +90,13 @@ async def process_read_message_event(self, event: Event, room_name: str):
8190
chat_id, other_user = await get_chat_and_user_ids_from_content(
8291
event.content, self.user
8392
)
84-
msg = await sync_to_async(DirectChatMessage.objects.get)(
85-
pk=event.content["message_id"]
86-
)
93+
msg = await orm_get(DirectChatMessage.objects, pk=event.content["message_id"])
8794
if msg.chat_id != chat_id or msg.author_id != other_user.id:
8895
raise WrongChatIdException(
8996
"Some of chat/message ids are wrong, you can't access this message"
9097
)
9198
msg.is_read = True
92-
await sync_to_async(msg.save)()
99+
await orm_save(msg, update_fields=["is_read"])
93100
# send 2 events to user's channel
94101
other_user_channel = cache.get(get_user_channel_cache_key(other_user), None)
95102
json_thingy = {
@@ -109,13 +116,13 @@ async def process_read_message_event(self, event: Event, room_name: str):
109116
async def process_delete_message_event(self, event: Event, room_name: str):
110117
message_id = event.content["message_id"]
111118

112-
message = await sync_to_async(DirectChatMessage.objects.get)(pk=message_id)
119+
message = await orm_get(DirectChatMessage.objects, pk=message_id)
113120

114121
if self.user.id != message.author_id:
115122
raise UserIsNotAuthor(f"User {self.user.id} is not author {message.text}")
116123

117124
message.is_deleted = True
118-
await sync_to_async(message.save)()
125+
await orm_save(message, update_fields=["is_deleted"])
119126

120127
chat_id, other_user = await get_chat_and_user_ids_from_content(
121128
event.content, self.user
@@ -144,24 +151,25 @@ async def process_edit_message_event(self, event, room_name):
144151
chat_id = DirectChat.get_chat_id_from_users(self.user, other_user)
145152

146153
# check if chat exists ( this raises exception if not )
147-
await sync_to_async(DirectChat.objects.get)(pk=chat_id)
154+
await orm_get(DirectChat.objects, pk=chat_id)
148155

149-
msg = await sync_to_async(DirectChatMessage.objects.get)(
150-
pk=event.content["message_id"]
151-
)
156+
msg = await orm_get(DirectChatMessage.objects, pk=event.content["message_id"])
152157

153-
message_author = await sync_to_async(lambda: msg.author)()
154-
if message_author != self.user:
158+
if msg.author_id != self.user.id:
155159
raise UserNotMessageAuthorException(
156160
f"User {self.user.id} is not author of message {msg.id}"
157161
)
158162
msg.text = event.content["text"]
159163
msg.is_edited = True
160-
await sync_to_async(msg.save)()
164+
await orm_save(msg, update_fields=["text", "is_edited"])
161165

162-
message_data = await sync_to_async(
163-
lambda: (DirectChatMessageListSerializer(msg)).data
164-
)()
166+
serialized_message = await orm_get(
167+
DirectChatMessage.objects.select_related("author", "reply_to__author").prefetch_related(
168+
"file_to_message__file"
169+
),
170+
pk=msg.pk,
171+
)
172+
message_data = DirectChatMessageListSerializer(serialized_message).data
165173
content = {
166174
"chat_id": chat_id,
167175
"message": message_data,

0 commit comments

Comments
 (0)