Currently creating independent SPA-s for user and support.
Typical flow:
- User attempts to register themselves to the live support chat system
- They get notified to wait (stay reactive!)
- Support specialists receive a notification on UI about pending user
- One of the support specialists clicks on the button and chat session starts
Service oriented architecture with two backends:
ChatServer:- publishes user join requests in
Redis pub/sub,userschannel - enables
UserandSupportto exchange messages via chat usingWebSockets
- publishes user join requests in
Subscriber:- subscribes to
userschannel inRedis pub/suband forwards user join requests to UI usingWebSockets
- subscribes to
Requirements for running the whole project:
- Docker engine
Instructions:
- clone the repo
- cd into repo
- start docker engine
- run:
docker-compose up(it startsChatServer,SubscriberandRedisin three different docker containers which are connected via docker network)
Protocol and flow description for building UI:
- Register
UserinChatServerby sending HTTP request to:GET localhost:9000/user/join/{userName}- Expect
JSONresponse:
{ "userId": "9d6d2db0cdb84d8ba67105670d94d641", "username": "Nika", "chatId": "d2464b5386044daf9e36ffd414260f67" } - Expect
- Initiate WebSocket connection for
UserinChatServerby sending request to:GET localhost:9000/chat/{chatId}- server immediately starts sending
pingmessage for each second, UI must respond with:pong:user(covers heartbeat functionality) - for requesting to join, send message:
{ "type": "Join", "args": { "userId": "9d6d2db0cdb84d8ba67105670d94d641", "username": "Nika", "from": "User" } }- Expect
JSONresponse:
{ "type": "UserJoined", "args": { "username": "Nika", "userId": "9d6d2db0cdb84d8ba67105670d94d641", "chatId": "d2464b5386044daf9e36ffd414260f67" } } - server immediately starts sending
- Initiate WebSocket connection for
SupportinSubscriberby sending request to:GET localhost:9001/users- server immediately starts sending
pingmessage for each second, UI must respond with:pong:support(covers heartbeat functionality) - for loading pending users send this WS message, it also subscribes to
Redis PubSuband reads newly joined users{ "type": "Load" } - Expect one re more
JSONWebSocket messages (TODO - consider sending one message with list of users instead of single message for each user): for pending usersfor newly joined users{ "username": "Nika", "userId": "9d6d2db0cdb84d8ba67105670d94d641", "chatId": "d2464b5386044daf9e36ffd414260f67" }{ "type": "UserJoined", "args": { "username": "Dodo", "userId": "b646273c1f8840b1ad296b40ef62f2c5", "chatId": "2500f760be124d299f214c5f3aa0ecf2" } } - once
Supportclicks to the special user request, UI must send theJoinUseras websocket message so that it gets filtered out for otherSupportagents, at the same time UI must send another request toChatServer-step 4(details instep 4)JoinUserlooks like
as a response the backend sends following WebSocket message:{ "type": "JoinUser", "args": { "userId": "8e0a7c3e1b2f489f8ce129a5167f71b5", "chatId": "0765d196ec4e4d108b9e4b6fca7c8254", "username": "Zaza" } }This message will be broadcasted to all support UI-s and Frontend will be able to drop this user from the list{ "type": "RemoveUser", "args": { "userId": "8e0a7c3e1b2f489f8ce129a5167f71b5" } } - server immediately starts sending
- Initiate WebSocket connection for
SupportinChatServerby sending request to:GET localhost:9000/chat/{chatId}- attach request body:
{ "type": "Join", "args": { "userId" : "9d6d2db0cdb84d8ba67105670d94d641", "supportUserName": "Vika", "username": "Nika", "from": "Support" } }
- Expect
JSONresponse:{ "type": "SupportJoinedUser", "args": { "supportId": "1939d1c378ea45809521c16668c21c10", "supportUserName": "Kiku", "username": "f3eb5276f29c4971827d31acd12327a7", "userId": "Gela", "chatId": "91a68f25d5e5444aaaa5dc47ebf7d893" } }
- Send chat message to
SupportfromUserby sending the following WebSocket text message on the opened WS connection:{ "type": "ChatMessage", "args": { "userId": "9d6d2db0cdb84d8ba67105670d94d641", "supportId": "7c270542e64a4dfbb2bc1c7793746674", "content": "Hey, I want to transfer money offshore, how can I do it via internet bank?", "from": "User" } }- Expect
JSONresponse for bothUserandSupportWebSocket connections:
{ "type": "ChatMessage", "args": { "userId": "9d6d2db0cdb84d8ba67105670d94d641", "supportId": "7c270542e64a4dfbb2bc1c7793746674", "content": "hey", "timestamp": "2023-10-22T18:18:59.360855Z", "from": "User" } } - Expect
- Send chat message to
UserfromSupportby sending the following WebSocket text message on the opened WS connection:{ "type": "ChatMessage", "args": { "userId": "9d6d2db0cdb84d8ba67105670d94d641", "supportId": "7c270542e64a4dfbb2bc1c7793746674", "supportUserName": "Vika", "content": "Hello, please navigate to Transfer and then select Offshore :)", "from": "Support" } }- Expect
JSONresponse for bothUserandSupportWebSocket connections:
{ "type": "ChatMessage", "args": { "userId": "f49f3cc665af4cb38092af714a4c87fa", "supportId": "7c270542e64a4dfbb2bc1c7793746674", "content": "Hello, please navigate to Transfer and then select Offshore :)", "timestamp": "2023-10-22T18:19:19.119515Z", "from": "Support" } } - Expect
Each chat expires after two minutes of inactivity. Also, WebSocket connection automatically closes after 120 seconds of inactivity.
In case User or Support refreshes browser backend either loads:
- conversation history (if the chat is still active)
- message about chat expiration (if the chat was expired)
-
In case
Userrefreshes browser the UI must send newJoin(re-join) WebSocket text message:{ "type": "Join", "args": { "userId": "8b5ee49aa3bb45e1a8719179e5e25c12", "supportId": "7c270542e64a4dfbb2bc1c7793746674", "username": "Nika", "from": "User" } }- Expect
JSONresponse:
{ "type": "ChatHistory", "args": { "messages": [ { "userId": "718b568c22f6460ca46a07cfc6aae3ba", "supportId": "35eda9304e554514915fdbb8f26b710d", "content": "Hey, I want to tranasfer money offshore, how can I do it via internet bank?", "timestamp": "2023-10-22T18:36:54.834408Z", "from": "User" }, { "userId": "718b568c22f6460ca46a07cfc6aae3ba", "supportId": "35eda9304e554514915fdbb8f26b710d", "content": "Hello, please navigate to Transfer and then select Offshore :)", "timestamp": "2023-10-22T18:37:10.219775Z", "from": "Support" } ] } } - Expect
-
Same message is loaded for
Support, however a bit differentJoin(re-join) WebSocket text message must be sent:{ "type": "Join", "args": { "userId": "8b5ee49aa3bb45e1a8719179e5e25c12", "supportId": "7c270542e64a4dfbb2bc1c7793746674", "username": "Nika", "supportUserName": "Vika", "from": "Support" } }



