From 4a9118c58e78c6a8768597ecb1ff17e4892f4c75 Mon Sep 17 00:00:00 2001 From: Igor Date: Sat, 4 May 2024 20:50:24 +0300 Subject: [PATCH 1/4] feat(ChatService): add java docs --- .../chatservice/chat/ChatService.java | 25 +++++++++++++++++-- .../chatservice/chat/duo/CompanionDto.java | 23 +++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 backend/chat-service/src/main/java/org/linkwave/chatservice/chat/duo/CompanionDto.java diff --git a/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/ChatService.java b/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/ChatService.java index 7ee1c806..a64149b6 100644 --- a/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/ChatService.java +++ b/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/ChatService.java @@ -80,7 +80,16 @@ public interface ChatService { */ void updateChat(@NonNull Chat chat); - Map> getChatsMembers(Long userId, List chatId); + /** + * Returns members for each chat that user (userId) is a member of. Every chat from the {@code chatIds} list + * is checked to determine if user is a member too. When it is, the members of that chat is retrieved, otherwise + * the chat is gonna to be skipped. + * + * @param userId id of the user + * @param chatIds chats to return members for + * @return map where key is chat id, and value is list of members of this chat + */ + Map> getChatsMembers(Long userId, List chatIds); /** * Used to get to know if user is a member of specific chat. @@ -115,6 +124,14 @@ public interface ChatService { boolean isAdmin(Long memberId, @NonNull Chat chat); + /** + * Checks whether member has specified role in the chat. + * + * @param chat duo / group chat + * @param memberId member that is needed to check role for + * @param role role is needed to check + * @throws ChatMemberPermissionsDenied when member not found or does not have the specified role + */ void checkMemberRole(@NonNull Chat chat, Long memberId, ChatRole role) throws ChatMemberPermissionsDenied; ChatMemberDto addGroupChatMember(String chatId, @NonNull RequestInitiator initiator); @@ -146,7 +163,11 @@ GroupChatDetailsDto getGroupChatDetails(@NonNull RequestInitiator initiator, Str */ void changeGroupChatAvatar(String chatId, @NonNull MultipartFile avatar); - boolean isAvatarSet(GroupChat chat); + /** + * @param chat non-null group chat object + * @return true if avatar is set, otherwise false + */ + boolean isAvatarSet(@NonNull GroupChat chat); /** * Retrieve a group chat avatar as array of bytes. diff --git a/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/duo/CompanionDto.java b/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/duo/CompanionDto.java new file mode 100644 index 00000000..5a1bc660 --- /dev/null +++ b/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/duo/CompanionDto.java @@ -0,0 +1,23 @@ +package org.linkwave.chatservice.chat.duo; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +import java.time.ZonedDateTime; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder +public class CompanionDto { + + private Long id; + private String username; + private String name; + private ZonedDateTime lastSeen; + + @JsonProperty("online") + private boolean isOnline; + +} From 8b29d53fd3ab51472402d24ac04b300cbb54b82b Mon Sep 17 00:00:00 2001 From: Igor Date: Sat, 4 May 2024 20:52:13 +0300 Subject: [PATCH 2/4] feat: use companion dto --- .../java/org/linkwave/chatservice/chat/ChatServiceImpl.java | 1 + .../java/org/linkwave/chatservice/chat/duo/DuoChatDto.java | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/ChatServiceImpl.java b/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/ChatServiceImpl.java index 58ca9c7b..50912642 100644 --- a/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/ChatServiceImpl.java +++ b/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/ChatServiceImpl.java @@ -12,6 +12,7 @@ import org.linkwave.chatservice.api.ws.LoadChatRequest; import org.linkwave.chatservice.api.ws.WSServiceClient; import org.linkwave.chatservice.chat.duo.Chat; +import org.linkwave.chatservice.chat.duo.CompanionDto; import org.linkwave.chatservice.chat.duo.DuoChatDto; import org.linkwave.chatservice.chat.duo.NewChatRequest; import org.linkwave.chatservice.chat.group.GroupChat; diff --git a/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/duo/DuoChatDto.java b/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/duo/DuoChatDto.java index 50a89eb1..ec93339b 100644 --- a/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/duo/DuoChatDto.java +++ b/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/duo/DuoChatDto.java @@ -4,7 +4,6 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.SuperBuilder; -import org.linkwave.chatservice.api.users.UserDto; import org.linkwave.chatservice.chat.ChatDto; @NoArgsConstructor @@ -13,6 +12,6 @@ @SuperBuilder public class DuoChatDto extends ChatDto { - private UserDto user; + private CompanionDto user; } From 1b04e5f26e2eacb272080ee0b560fabc4b5f439f Mon Sep 17 00:00:00 2001 From: Igor Date: Sat, 4 May 2024 20:53:13 +0300 Subject: [PATCH 3/4] refactor(ChatService): map chats in separate method --- .../chatservice/chat/ChatServiceImpl.java | 103 +++++++++--------- 1 file changed, 53 insertions(+), 50 deletions(-) diff --git a/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/ChatServiceImpl.java b/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/ChatServiceImpl.java index 50912642..1725cf0c 100644 --- a/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/ChatServiceImpl.java +++ b/backend/chat-service/src/main/java/org/linkwave/chatservice/chat/ChatServiceImpl.java @@ -185,46 +185,7 @@ public Pair> getUserChats(@NonNull RequestInitiator initiato final Set usersIds = new HashSet<>(); final List selectedChats = userChats .stream() - .map(chat -> { - final boolean isGroupChat = chat instanceof GroupChat; - final Class cls = isGroupChat - ? GroupChatDto.class - : DuoChatDto.class; - - final ChatDto chatDto = modelMapper.map(chat, cls); - - if (isGroupChat) { - chatDto.setAvatarAvailable(isAvatarSet((GroupChat) chat)); - } else { - // pull user info for duo chat - final List members = chat.getMembers(); - Long memberId = members.get(0).getId(); - usersIds.add(memberId.equals(initiator.userId()) - ? memberId = members.get(1).getId() - : memberId - ); - - ((DuoChatDto) chatDto).setUser( - UserDto.builder().id(memberId).build() - ); - } - - final Message lastMessage = chat.getLastMessage(); - if (lastMessage == null) { - return chatDto; - } - - final MessageDto messageDto = lastMessage.convert(modelMapper); - - // save author ID for filling user data in the future - messageDto.setAuthor(MessageAuthorDto.builder() - .id(lastMessage.getAuthorId()) - .build()); - chatDto.setLastMessage(messageDto); - - usersIds.add(lastMessage.getAuthorId()); - return chatDto; - }) + .map(chat -> mapChats(chat, usersIds, initiator)) .toList(); final Map usersMap = new LinkedHashMap<>(); @@ -239,9 +200,7 @@ public Pair> getUserChats(@NonNull RequestInitiator initiato ); // pull contacts - final Map contactsMap = fetchAllContacts(initiator) - .stream() - .collect(toMap(contact -> contact.getUser().getId(), identity())); + final Map contactsMap = fetchAllContacts(initiator); selectedChats.forEach(chat -> { final MessageDto lastMessage = chat.getLastMessage(); @@ -256,10 +215,12 @@ public Pair> getUserChats(@NonNull RequestInitiator initiato if (chat instanceof DuoChatDto duoChat) { duoChat.setAvatarAvailable(user.getAvatarPath() != null); + + // inject companion dto final Long companionId = duoChat.getUser().getId(); - final UserDto companionUserDto = usersMap.get(companionId); - if (companionUserDto != null) { - duoChat.setUser(companionUserDto); + final UserDto userDto = usersMap.get(companionId); + if (userDto != null) { + duoChat.setUser(modelMapper.map(userDto, CompanionDto.class)); } } @@ -279,7 +240,7 @@ public Pair> getUserChats(@NonNull RequestInitiator initiato } @NonNull - private List fetchAllContacts(@NonNull RequestInitiator initiator) { + private Map fetchAllContacts(@NonNull RequestInitiator initiator) { final String username = ""; // any username is matched int offset = 0; @@ -291,7 +252,49 @@ private List fetchAllContacts(@NonNull RequestInitiator initiator) { allContacts.addAll(contacts); offset += DEFAULT_BATCH_SIZE; } while (contacts.size() == DEFAULT_BATCH_SIZE); - return allContacts; + return allContacts.stream() + .collect(toMap(contact -> contact.getUser().getId(), identity())); + } + + private ChatDto mapChats(Chat chat, Set usersIds, RequestInitiator initiator) { + final boolean isGroupChat = chat instanceof GroupChat; + final Class cls = isGroupChat + ? GroupChatDto.class + : DuoChatDto.class; + + final ChatDto chatDto = modelMapper.map(chat, cls); + + if (isGroupChat) { + chatDto.setAvatarAvailable(isAvatarSet((GroupChat) chat)); + } else { + // pull user info for duo chat + final List members = chat.getMembers(); + Long memberId = members.get(0).getId(); + usersIds.add(memberId.equals(initiator.userId()) + ? memberId = members.get(1).getId() + : memberId + ); + + ((DuoChatDto) chatDto).setUser( + CompanionDto.builder().id(memberId).build() + ); + } + + final Message lastMessage = chat.getLastMessage(); + if (lastMessage == null) { + return chatDto; + } + + final MessageDto messageDto = lastMessage.convert(modelMapper); + + // save author ID for filling user data in the future + messageDto.setAuthor(MessageAuthorDto.builder() + .id(lastMessage.getAuthorId()) + .build()); + chatDto.setLastMessage(messageDto); + + usersIds.add(lastMessage.getAuthorId()); + return chatDto; } @Override @@ -308,8 +311,8 @@ public void updateChat(@NonNull Chat chat) { } @Override - public Map> getChatsMembers(Long userId, List chatId) { - return chatRepository.findAllById(chatId) + public Map> getChatsMembers(Long userId, List chatIds) { + return chatRepository.findAllById(chatIds) .stream() .filter(chat -> isMember(userId, chat)) .collect(toMap(Chat::getId, Chat::getMembers)); From 4e18e83a689fc236c6873f9bc38fdf3ff7e264e6 Mon Sep 17 00:00:00 2001 From: Igor Date: Sat, 4 May 2024 20:53:33 +0300 Subject: [PATCH 4/4] docs(openapi): update chats fetch --- .../src/main/resources/static/openapi.json | 102 +++++++++++++++++- 1 file changed, 97 insertions(+), 5 deletions(-) diff --git a/backend/chat-service/src/main/resources/static/openapi.json b/backend/chat-service/src/main/resources/static/openapi.json index 0f76e5eb..98bb774b 100644 --- a/backend/chat-service/src/main/resources/static/openapi.json +++ b/backend/chat-service/src/main/resources/static/openapi.json @@ -48,7 +48,7 @@ "items": { "anyOf": [ { - "$ref": "#/components/schemas/ChatDto" + "$ref": "#/components/schemas/DuoChatDto" }, { "$ref": "#/components/schemas/GroupChatDto" @@ -1034,6 +1034,10 @@ "type": "number", "nullable": false }, + "avatarAvailable": { + "type": "boolean", + "nullable": false + }, "lastMessage": { "$ref": "#/components/schemas/TextMessageDto" } @@ -1042,6 +1046,62 @@ "id": "65ed725dc636007bd4027c80", "type": "DUO", "createdAt": 1710060125.916620300, + "avatarAvailable": true, + "lastMessage": { + "id": "66055236cb761a59d173d978", + "action": "MESSAGE", + "createdAt": 1711624758.135000000, + "author": { + "id": 1, + "username": "@mathewmcc", + "name": "Matthew McConaughey" + }, + "reactions": [], + "text": "Hi everyone!", + "edited": false, + "isRead": false + } + } + }, + "DuoChatDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "nullable": false + }, + "type": { + "type": "string", + "nullable": false, + "example": "DUO" + }, + "createdAt": { + "type": "number", + "nullable": false + }, + "avatarAvailable": { + "type": "boolean", + "nullable": false + }, + "user": { + "$ref": "#/components/schemas/CompanionDto" + }, + "lastMessage": { + "$ref": "#/components/schemas/TextMessageDto" + } + }, + "example": { + "id": "65ed725dc636007bd4027c80", + "type": "DUO", + "createdAt": 1710060125.916620300, + "avatarAvailable": true, + "user": { + "id": 22, + "username": "@emmtlor", + "name": "Emma Taylor", + "lastSeen": 1707606183.446933000, + "online": false + }, "lastMessage": { "id": "66055236cb761a59d173d978", "action": "MESSAGE", @@ -1096,9 +1156,9 @@ "type": "string", "nullable": false }, - "avatarPath": { - "type": "string", - "nullable": true + "avatarAvailable": { + "type": "boolean", + "nullable": false }, "createdAt": { "type": "number", @@ -1112,7 +1172,7 @@ "id": "65e8b0e4404b9356a273e305", "type": "GROUP", "name": "My group chat", - "avatarPath": null, + "avatarAvailable": true, "createdAt": 1709748452.204932100, "lastMessage": { "id": "66055236cb761a59d173d978", @@ -1466,6 +1526,38 @@ "contentType": "application/zip", "size": 5012275 } + }, + "CompanionDto": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "nullable": false + }, + "username": { + "type": "string", + "nullable": false + }, + "name": { + "type": "string", + "nullable": false + }, + "lastSeen": { + "type": "number", + "nullable": false + }, + "online": { + "type": "boolean", + "nullable": false + } + }, + "example": { + "id": 22, + "username": "@emmtlor", + "name": "Emma Taylor", + "lastSeen": 1707606183.446933000, + "online": false + } } }, "headers": {