From c7639f38aed711526dcecfbf92db28d1ce493d36 Mon Sep 17 00:00:00 2001 From: knst-kotov Date: Sun, 23 Jan 2022 14:02:57 +0300 Subject: [PATCH] use redis for storing messages --- redis/client.py | 63 ++++++++++++++++++++++--------------------- resolvers/inbox.py | 67 ++++++++++++++++++++++++++++++++++++---------- schema.graphql | 35 +++++++++++++----------- 3 files changed, 105 insertions(+), 60 deletions(-) diff --git a/redis/client.py b/redis/client.py index ae5372f7..aaf72ca3 100644 --- a/redis/client.py +++ b/redis/client.py @@ -6,44 +6,47 @@ from settings import REDIS_URL class Redis: - def __init__(self, uri=REDIS_URL): - self._uri: str = uri - self._instance = None + def __init__(self, uri=REDIS_URL): + self._uri: str = uri + self._instance = None - async def connect(self): - if self._instance is not None: - return - self._instance = aioredis.from_url(self._uri, encoding="utf-8") + async def connect(self): + if self._instance is not None: + return + self._instance = aioredis.from_url(self._uri, encoding="utf-8") - async def disconnect(self): - if self._instance is None: - return - self._instance.close() - await self._instance.wait_closed() - self._instance = None + async def disconnect(self): + if self._instance is None: + return + self._instance.close() + await self._instance.wait_closed() + self._instance = None - async def execute(self, command, *args, **kwargs): - return await self._instance.execute_command(command, *args, **kwargs) + async def execute(self, command, *args, **kwargs): + return await self._instance.execute_command(command, *args, **kwargs) + + async def lrange(self, name, start, end): + return await self._instance.lrange(name, start, end) async def test(): - redis = Redis() - from datetime import datetime + redis = Redis() + from datetime import datetime - await redis.connect() - await redis.execute("SET", "1-KEY1", 1) - await redis.execute("SET", "1-KEY2", 1) - await redis.execute("SET", "1-KEY3", 1) - await redis.execute("SET", "1-KEY4", 1) - await redis.execute("EXPIREAT", "1-KEY4", int(datetime.utcnow().timestamp())) - v = await redis.execute("KEYS", "1-*") - print(v) - await redis.execute("DEL", *v) - v = await redis.execute("KEYS", "1-*") - print(v) + await redis.connect() + await redis.execute("SET", "1-KEY1", 1) + await redis.execute("SET", "1-KEY2", 1) + await redis.execute("SET", "1-KEY3", 1) + await redis.execute("SET", "1-KEY4", 1) + await redis.execute("EXPIREAT", "1-KEY4", int(datetime.utcnow().timestamp())) + v = await redis.execute("KEYS", "1-*") + print(v) + await redis.execute("DEL", *v) + v = await redis.execute("KEYS", "1-*") + print(v) if __name__ == '__main__': - import asyncio + import asyncio - asyncio.run(test()) + asyncio.run(test()) diff --git a/resolvers/inbox.py b/resolvers/inbox.py index 07a7f6fa..c6650af2 100644 --- a/resolvers/inbox.py +++ b/resolvers/inbox.py @@ -5,7 +5,10 @@ from resolvers.base import mutation, query, subscription from auth.authenticate import login_required -import asyncio +import asyncio, uuid, json +from datetime import datetime + +from redis import redis class MessageSubscriptions: lock = asyncio.Lock() @@ -32,22 +35,58 @@ class MessageResult: self.status = status self.message = message +@mutation.field("createChat") +@login_required +async def create_chat(_, info, description): + user = info.context["request"].user + + chat_id = uuid.uuid4() + chat = { + "description" : description, + "createdAt" : str(datetime.now), + "createdBy" : user.slug, + "id" : str(chat_id) + } + + await redis.execute("SET", f"chats/{chat_id}", json.dumps(chat)) + + return { "chatId" : chat_id } + +@query.field("enterChat") +@login_required +async def enter_chat(_, info, chatId): + chat = await redis.execute("GET", f"chats/{chatId}") + if not chat: + return { "error" : "chat not exist" } + chat = json.loads(chat) + + messages = await redis.lrange(f"chats/{chatId}/messages", 0, 10) + messages = [json.loads(msg) for msg in messages] + + return { + "chat" : chat, + "messages" : messages + } @mutation.field("createMessage") @login_required -async def create_message(_, info, body, replyTo = None): - auth = info.context["request"].auth - user_id = auth.user_id - - new_message = Message.create( - author = user_id, - body = body, - replyTo = replyTo - ) - - result = MessageResult("NEW", new_message) - await MessageSubscriptions.put(result) - +async def create_message(_, info, chatId, body, replyTo = None): + user = info.context["request"].user + + chat = await redis.execute("GET", f"chats/{chatId}") + if not chat: + return { "error" : "chat not exist" } + + new_message = { + "chatId" : chatId, + "author" : user.slug, + "body" : body, + "replyTo" : replyTo + } + + message_id = await redis.execute("LPUSH", f"chats/{chatId}/messages", json.dumps(new_message)) + new_message["id"] = message_id + return {"message" : new_message} @query.field("getMessages") diff --git a/schema.graphql b/schema.graphql index 2cd3ade9..4bf9c190 100644 --- a/schema.graphql +++ b/schema.graphql @@ -64,19 +64,20 @@ enum MessageStatus { DELETED } -type CreateChatResult { - chatId: Int - error: String -} - type MessageWithStatus { status: MessageStatus! message: Message! } -type ChatRoomResult { - messages: [Message]! - room: ChatRoom! +type CreateChatResult { + chatId: String + error: String +} + +type EnterChatResult { + chat: Chat + messages: [Message] + error: String } input TopicInput { @@ -97,11 +98,10 @@ type TopicResult { type Mutation { # message - createChat: CreateChatResult! - getRoom(chatRoom: Int!): ChatRoomResult! # TODO: private rooms protection - createMessage(body: String!, replyTo: Int): MessageResult! - updateMessage(id: Int!, body: String!): MessageResult! - deleteMessage(messageId: Int!): Result! + createChat(description: String): CreateChatResult! + createMessage(chatId: String!, body: String!, replyTo: Int): MessageResult! + updateMessage(chatId: String!, id: Int!, body: String!): MessageResult! + deleteMessage(chatId: String!, messageId: Int!): Result! # auth confirmEmail(token: String!): AuthResult! @@ -178,13 +178,16 @@ type Query { # communities getCommunity(slug: String): Community! getCommunities: [Community]! + + #messages + enterChat(chatId: String!): EnterChatResult! } ############################################ Subscription type Subscription { messageChanged: MessageWithStatus! - chatUpdated: ChatRoomResult! + chatUpdated: MessageWithStatus! onlineUpdated: [User!]! shoutUpdated: Shout! userUpdated: User! @@ -267,11 +270,11 @@ type Message { visibleForUsers: [Int]! } -type ChatRoom { +type Chat { id: Int! createdAt: DateTime! updatedAt: DateTime! - notes: String + description: String } type Comment {