Feature/polling in chat#236
Conversation
There was a problem hiding this comment.
Pull request overview
This PR extends the backend chat system so polls can be created and consumed as first-class chat elements in both group chats and private chats, by persisting poll creation as ChatMessage entries and exposing dedicated poll endpoints.
Changes:
- Generalize poll ownership via
PollContextand extendPollto optionally belong to either aGroupor aPrivateChat. - Introduce
MessageTypeand extendChatMessage/ChatMessageDtoso messages can represent either encrypted text or an embedded poll payload. - Add a private-chat poll controller and refactor existing chat/poll controllers to use shared
PollService/ChatServiceDTO mapping.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| backend/src/main/java/vaultWeb/services/PollService.java | Adds private-chat poll support and persists poll creation into the chat message stream. |
| backend/src/main/java/vaultWeb/services/ChatService.java | Adds messageType handling and DTO conversion for TEXT vs POLL messages. |
| backend/src/main/java/vaultWeb/repositories/PollRepository.java | Adds query method to fetch polls by private chat. |
| backend/src/main/java/vaultWeb/models/PollContext.java | New record to model poll ownership context (group vs private chat). |
| backend/src/main/java/vaultWeb/models/Poll.java | Extends poll entity to optionally reference a private chat. |
| backend/src/main/java/vaultWeb/models/enums/MessageType.java | Introduces message type enum (TEXT, POLL). |
| backend/src/main/java/vaultWeb/models/ChatMessage.java | Adds messageType and optional poll reference on chat messages. |
| backend/src/main/java/vaultWeb/dtos/ChatMessageDto.java | Adds messageType and poll fields for message stream integration. |
| backend/src/main/java/vaultWeb/controllers/PrivateChatPollController.java | New REST controller for polls inside private chats. |
| backend/src/main/java/vaultWeb/controllers/PrivateChatController.java | Refactors message DTO mapping to use ChatService.toDto(...). |
| backend/src/main/java/vaultWeb/controllers/GroupController.java | No functional changes; appears to be formatting-only adjustments. |
| backend/src/main/java/vaultWeb/controllers/GroupChatPollController.java | Updates group poll endpoints to use PollContext + shared service methods. |
| backend/src/main/java/vaultWeb/controllers/ChatController.java | Refactors WebSocket responses to use ChatService.toDto(...). |
Comments suppressed due to low confidence (1)
backend/src/main/java/vaultWeb/controllers/GroupChatPollController.java:122
groupIdis part of the URL (/groups/{groupId}/polls/...) but is not bound/used in vote/update/delete endpoints anymore, so the API no longer verifies thatpollIdactually belongs to the{groupId}in the path. This is a regression from the previous version and can lead to confusing/incorrect URLs being accepted.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (dto.getMessageType() == MessageType.TEXT) { | ||
| if (dto.getE2eePayload() == null | ||
| || dto.getE2eePayload().isBlank() | ||
| || dto.getSenderDeviceId() == null | ||
| || dto.getSenderDeviceId().isBlank()) { | ||
| throw new IllegalArgumentException( | ||
| "Missing end-to-end encrypted payload or sender device ID"); | ||
| } | ||
| } | ||
|
|
| dto.setMessageType(message.getMessageType()); | ||
|
|
||
| if (message.getGroup() != null) { | ||
|
|
||
| dto.setGroupId(message.getGroup().getId()); | ||
| } | ||
|
|
||
| if (message.getPrivateChat() != null) { | ||
|
|
||
| dto.setPrivateChatId(message.getPrivateChat().getId()); | ||
| } | ||
|
|
||
| if (message.getMessageType() == MessageType.TEXT) { | ||
|
|
||
| dto.setE2eePayload(message.getE2eePayload()); | ||
|
|
||
| } else if (message.getMessageType() == MessageType.POLL) { | ||
|
|
||
| dto.setPoll(pollService.toResponseDto(message.getPoll())); | ||
| } |
| public List<Poll> getPollsByPrivateChat(Long privateChatId, User currentUser) { | ||
| PrivateChat privateChat = | ||
| privateChatRepository | ||
| .findById(privateChatId) | ||
| .orElseThrow( | ||
| () -> | ||
| new GroupNotFoundException( | ||
| "Private chat with id " + privateChatId + " not found")); | ||
|
|
||
| return pollRepository.findByPrivateChatId(privateChatId); | ||
| } |
| if (!poll.getAuthor().getId().equals(user.getId())) { | ||
| throw new UnauthorizedException("Only the author can delete the poll"); | ||
| } | ||
|
|
||
| pollRepository.delete(poll); |
| public PollContext { | ||
| if (isGroup() && isPrivateChat()) { | ||
| throw new IllegalArgumentException("PollConext must contain either group or private chat"); | ||
| } | ||
| } |
There was a problem hiding this comment.
isGroup() and isPrivateChat() already checking the null, basically do the same thing. I will only fix the typo here for the exception msg
| import vaultWeb.services.auth.AuthService; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/private-chats/{privateChatId}/polls") |
There was a problem hiding this comment.
I am planning to have a base from the configuration. Can be a future task. Okay?
| public ResponseEntity<Void> vote(@PathVariable Long pollId, @PathVariable Long optionId) { | ||
| User currentUser = authService.getCurrentUser(); | ||
| pollService.vote(pollId, optionId, currentUser); | ||
| return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); |
| privateChatRepository | ||
| .findById(dto.getPrivateChatId()) | ||
| .orElseThrow(() -> new PrivateChatNotFoundException("Private chat not found")); | ||
| message.setMessageType(dto.getMessageType()); |
| * specified group/private chat does not exist. | ||
| * @throws IllegalArgumentException if encrypted payload metadata is missing. | ||
| */ | ||
| public ChatMessage saveMessage(ChatMessageDto dto) { |
DenizAltunkapan
left a comment
There was a problem hiding this comment.
@Navila48 thanks a lot for this! Splitting the polls into separate group and private chat controllers with a shared service looks like a clean approach.
Before I can approve, could you please fix the failing workflows? The Spotless check is red because the files were rewritten with CRLF line endings — that's also why the diff shows every file fully deleted and re-added instead of just the real changes. Running mvn spotless:apply and normalizing the line endings back to LF should fix both the CI and the noisy diff.
Also please have a look at Copilot's comments on the message type handling and the private chat poll access — those are worth addressing too. Left a couple of small notes below.
| } else if (pollContext.isPrivateChat()) { | ||
| PrivateChat privateChat = pollContext.privateChat(); | ||
| boolean isChatParticipant = | ||
| (privateChat.getUser1().getId().equals(user.getId())) |
There was a problem hiding this comment.
Small thing: getUser1() / getUser2() could be null here, so this would throw a NullPointerException instead of the intended UnauthorizedException. Could you add a null check like we do in getAuthorizedPrivateChat() in PrivateChatController?
| public record PollContext(Group group, PrivateChat privateChat) { | ||
| public PollContext { | ||
| if (isGroup() && isPrivateChat()) { | ||
| throw new IllegalArgumentException("PollConext must contain either group or private chat"); |
There was a problem hiding this comment.
tiny typo: "PollConext" -> "PollContext" : )
Hi,
After that I have added the follwing changes on .gitAttributes file Then run the command : git add --renormalize . Then after restarting the IDE, I again apply spotless and unfortunately the same issue persist like it detects the whole file as modified, so it deleted(red marked) and rewritted(grren marked) it.
I have tried all the possible options to fix this but no luck. |
|
Hey @Navila48, thanks for digging into this! 🙏 Your likely problem: the repo has no The reliable fix is to pin LF so it no longer depends on anyone's OS/IDE. In <configuration>
<lineEndings>UNIX</lineEndings>
<java>
...Then clean up the branch: git config core.autocrlf false
git config core.eol lf
git add .gitattributes && git commit -m "chore: add .gitattributes (LF)"
git add --renormalize .
git commit -m "chore: normalize line endings to LF"
mvn -pl backend spotless:apply && git commit -am "style: spotless"To verify it's clean (should print nothing): git ls-files --eol backend/src | grep crlfAfter that the diff should collapse down to your real changes. 👍 |
Thanks a lot. You are the best 👍 |


Summary
Added necessary changes for the backend part
Linked issue
218