Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package com.codzilla.backend.Authentication.AuthController;

import com.codzilla.backend.Authentication.JWTUtils.JWTUtils;
import com.codzilla.backend.Authentication.dto.*;
import com.codzilla.backend.User.User;
import com.codzilla.backend.User.UserService;
import com.codzilla.backend.Authentication.config.AuthSettings;
import com.codzilla.backend.Authentication.dto.LoginRequestDTO;
import com.codzilla.backend.Authentication.dto.LoginResponseDTO;
import com.codzilla.backend.Authentication.dto.RegisterRequestDTO;
import com.codzilla.backend.Authentication.dto.RegisterResponseDTO;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
Expand Down Expand Up @@ -64,7 +61,7 @@ public ResponseEntity<?> login(@RequestBody LoginRequestDTO request, HttpServlet
refreshCookie.setMaxAge((int) settings.getRefreshTokenTtl().toSeconds());
refreshCookie.setSecure(false);
response.addCookie(refreshCookie);
return ResponseEntity.ok(new LoginResponseDTO(user.getNickname()));
return ResponseEntity.ok(new UserResponseDTO(user));
}

@PostMapping("/logout")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.codzilla.backend.Authentication.dto;

import com.codzilla.backend.User.User;

import java.util.List;
import java.util.Objects;
import java.util.UUID;

public record UserResponseDTO(
Expand All @@ -9,4 +12,12 @@ public record UserResponseDTO(
UUID id,
List<String> authorities
) {
public UserResponseDTO(User user) {
this(
user.getNickname(),
user.getEmail(),
user.getId(),
user.getAuthorities().stream().map(Objects::toString).toList()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.codzilla.backend.PreMatch.MatchRoom;
package com.codzilla.backend.MatchRoom;

import com.codzilla.backend.PreMatch.DraftSession.DraftSession;
import com.codzilla.backend.PreMatch.model.Category;
Expand All @@ -24,7 +24,7 @@
@Setter
public class Match {

enum Status {
public enum Status {
DRAFTING,
LIVE,
FINISHED
Expand Down Expand Up @@ -77,7 +77,7 @@ public UUID opponentOf(UUID userId) {
return null;
}

Match(UUID firstUserId, UUID secondUserId) {
public Match(UUID firstUserId, UUID secondUserId) {
this.firstUserId = firstUserId;
this.secondUserId = secondUserId;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.codzilla.backend.PreMatch;
package com.codzilla.backend.MatchRoom;

import com.codzilla.backend.PreMatch.DTO.DraftSessionResponseDTO;
import com.codzilla.backend.PreMatch.DTO.MatchResultDTO;
import com.codzilla.backend.PreMatch.DTO.WebSocketDTO;
import com.codzilla.backend.PreMatch.MatchSettings;
import com.codzilla.backend.PreMatch.events.MatchResultNotifyEvent;
import com.codzilla.backend.PreMatch.MatchRoom.MatchService;
import com.codzilla.backend.PreMatch.events.DraftSessionFinishedEvent;
import com.codzilla.backend.PreMatch.events.DraftSessionUpdatedEvent;
import com.codzilla.backend.judge.submission.SubmissionResponseDTO;
import com.codzilla.backend.judge.submission.SubmissionUpdatedEvent;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.simp.SimpMessagingTemplate;
Expand Down Expand Up @@ -74,4 +76,26 @@ public void handleMatchResult(MatchResultNotifyEvent event) {
)
);
}

@Async
@EventListener
public void handleSubmissionUpdated(SubmissionUpdatedEvent submissionUpdatedEvent) {
var submission = submissionUpdatedEvent.submission();
messagingTemplate.convertAndSend(
matchSettings.getWebSocketMatchDestination(submission.getMatchId()),
new WebSocketDTO(
WebSocketDTO.Status.SUBMISSION,
new SubmissionResponseDTO(
submission.getId(),
submission.getProblemId(),
submission.getLanguageId(),
submission.getStatus().name(),
submission.getCreatedAt(),
submission.getUpdatedAt(),
submission.getResultDetails(),
submission.getUserId()
)
)
);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.codzilla.backend.PreMatch.MatchRoom;
package com.codzilla.backend.MatchRoom;

import org.springframework.data.jpa.repository.JpaRepository;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
package com.codzilla.backend.PreMatch.MatchRoom;
package com.codzilla.backend.MatchRoom;

import com.codzilla.backend.Rating.MatchFinishedEvent;
import com.codzilla.backend.PreMatch.DTO.WebSocketDTO;
import com.codzilla.backend.PreMatch.DraftSession.DraftSession;
import com.codzilla.backend.PreMatch.DraftSession.DraftSessionService;
import com.codzilla.backend.PreMatch.MatchSettings;
import com.codzilla.backend.PreMatch.events.DraftSessionFinishedEvent;
import com.codzilla.backend.PreMatch.model.Category;
import com.codzilla.backend.PreMatch.model.Language;
import com.codzilla.backend.PreMatch.model.ProblemType;
import com.codzilla.backend.judge.client.SqlServiceClient;
import com.codzilla.backend.judge.problem.Problem;
import com.codzilla.backend.judge.problem.ProblemRepository;
import com.codzilla.backend.judge.problem.ProblemService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.context.ApplicationEventPublisher;

import java.time.Instant;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.codzilla.backend.Authentication.Exceptions.UserNotFoundException;
import com.codzilla.backend.User.UserRepository;
import com.codzilla.backend.PreMatch.MatchRoom.MatchService;
import com.codzilla.backend.MatchRoom.MatchService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ public class WebSocketDTO {
public enum Status {
MATCH_STARTED_REDIRECT,
DRAFT,
LIVE,
MATCH_FINISHED
SUBMISSION,
MATCH_FINISHED,
}

public WebSocketDTO(Status status, Object payload) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class MatchSettings {
Duration timeToPick;
String websocketMatchPrefix;

String getWebSocketMatchDestination(UUID matchId) {
public String getWebSocketMatchDestination(UUID matchId) {
return websocketMatchPrefix + matchId;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package com.codzilla.backend.WebSocket;

import com.codzilla.backend.PreMatch.MatchRoom.Match;
import com.codzilla.backend.PreMatch.MatchRoom.MatchRepository;
import com.codzilla.backend.PreMatch.MatchRoom.MatchService;
import com.codzilla.backend.PreMatch.MatchSettings;
import com.codzilla.backend.User.User;
import com.codzilla.backend.MatchRoom.MatchRepository;
import com.codzilla.backend.User.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.codzilla.backend.judge.problem;

import com.codzilla.backend.PreMatch.MatchRoom.Match;
import com.codzilla.backend.PreMatch.MatchRoom.MatchService;
import com.codzilla.backend.MatchRoom.Match;
import com.codzilla.backend.MatchRoom.MatchService;
import com.codzilla.backend.PreMatch.model.Category;
import com.codzilla.backend.PreMatch.model.Language;
import com.codzilla.backend.User.User;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;

import java.time.LocalDateTime;
Expand All @@ -25,7 +22,7 @@
import java.util.stream.Collectors;

@RestController
@RequestMapping("/my-submissions")
@RequestMapping("/submissions")
@RequiredArgsConstructor
public class SubmissionController {

Expand All @@ -34,64 +31,81 @@ public class SubmissionController {
private final Map<UUID, ConcurrentLinkedQueue<DeferredResult<ResponseEntity<List<SubmissionResponseDTO>>>>> waiters = new ConcurrentHashMap<>();
private final UserRepository userRepository;

@GetMapping
public DeferredResult<ResponseEntity<List<SubmissionResponseDTO>>> getUserSubmissions(
@AuthenticationPrincipal User user,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime lastUpdate) {

DeferredResult<ResponseEntity<List<SubmissionResponseDTO>>> output = new DeferredResult<>(20000L);

if (user == null) {
output.setResult(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build());
return output;
}

User fullUser = userRepository.findByEmail(user.getEmail())
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
UUID userId = fullUser.getId();

output.onTimeout(() -> output.setResult(ResponseEntity.status(HttpStatus.NOT_MODIFIED).build()));

Optional<Submission> latestSub = submissionRepository.findFirstByUserIdOrderByUpdatedAtDesc(userId);
boolean hasUpdates = lastUpdate == null ||
(latestSub.isPresent() && latestSub.get().getUpdatedAt().isAfter(lastUpdate));

if (hasUpdates) {
output.setResult(ResponseEntity.ok(fetchUserSubmissions(userId)));
return output;
}

waiters.computeIfAbsent(userId, k -> new ConcurrentLinkedQueue<>()).add(output);

output.onCompletion(() -> {
ConcurrentLinkedQueue<DeferredResult<ResponseEntity<List<SubmissionResponseDTO>>>> queue = waiters.get(userId);
if (queue != null) {
queue.remove(output);
}
});

return output;
@GetMapping("/{matchId}")
ResponseEntity<?> getSubmissionsOfMatch(@PathVariable UUID matchId) {
return ResponseEntity.ok(
submissionRepository.findByMatchIdAndStatusNotIn(matchId, List.of(Submission.Status.PROCESSING, Submission.Status.IN_QUEUE))
.stream().map((sub) -> new SubmissionResponseDTO(
sub.getId(),
sub.getProblemId(),
sub.getLanguageId(),
sub.getStatus().name(),
sub.getCreatedAt(),
sub.getUpdatedAt(),
sub.getResultDetails(),
sub.getUserId()
))
);
}

@EventListener
public void onSubmissionUpdated(SubmissionUpdatedEvent event) {
ConcurrentLinkedQueue<DeferredResult<ResponseEntity<List<SubmissionResponseDTO>>>> queue = waiters.get(event.userId());

if (queue != null && !queue.isEmpty()) {
List<SubmissionResponseDTO> freshData = fetchUserSubmissions(event.userId());

DeferredResult<ResponseEntity<List<SubmissionResponseDTO>>> result;
while ((result = queue.poll()) != null) {
result.setResult(ResponseEntity.ok(freshData));
}
}
}

private List<SubmissionResponseDTO> fetchUserSubmissions(UUID userId) {
return submissionRepository
.findAllByUserIdOrderByCreatedAtDesc(userId)
.stream()
.map(SubmissionResponseDTO::fromEntity)
.collect(Collectors.toList());
}
// @GetMapping
// public DeferredResult<ResponseEntity<List<SubmissionResponseDTO>>> getUserSubmissions(
// @AuthenticationPrincipal User user,
// @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime lastUpdate) {
//
// DeferredResult<ResponseEntity<List<SubmissionResponseDTO>>> output = new DeferredResult<>(20000L);
//
// if (user == null) {
// output.setResult(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build());
// return output;
// }
//
// User fullUser = userRepository.findByEmail(user.getEmail())
// .orElseThrow(() -> new UsernameNotFoundException("User not found"));
// UUID userId = fullUser.getId();
//
// output.onTimeout(() -> output.setResult(ResponseEntity.status(HttpStatus.NOT_MODIFIED).build()));
//
// Optional<Submission> latestSub = submissionRepository.findFirstByUserIdOrderByUpdatedAtDesc(userId);
// boolean hasUpdates = lastUpdate == null ||
// (latestSub.isPresent() && latestSub.get().getUpdatedAt().isAfter(lastUpdate));
//
// if (hasUpdates) {
// output.setResult(ResponseEntity.ok(fetchUserSubmissions(userId)));
// return output;
// }
//
// waiters.computeIfAbsent(userId, k -> new ConcurrentLinkedQueue<>()).add(output);
//
// output.onCompletion(() -> {
// ConcurrentLinkedQueue<DeferredResult<ResponseEntity<List<SubmissionResponseDTO>>>> queue = waiters.get(userId);
// if (queue != null) {
// queue.remove(output);
// }
// });
//
// return output;
// }

// @EventListener
// public void onSubmissionUpdated(SubmissionUpdatedEvent event) {
// ConcurrentLinkedQueue<DeferredResult<ResponseEntity<List<SubmissionResponseDTO>>>> queue = waiters.get(event.userId());
//
// if (queue != null && !queue.isEmpty()) {
// List<SubmissionResponseDTO> freshData = fetchUserSubmissions(event.userId());
//
// DeferredResult<ResponseEntity<List<SubmissionResponseDTO>>> result;
// while ((result = queue.poll()) != null) {
// result.setResult(ResponseEntity.ok(freshData));
// }
// }
// }

// private List<SubmissionResponseDTO> fetchUserSubmissions(UUID userId) {
// return submissionRepository
// .findAllByUserIdOrderByCreatedAtDesc(userId)
// .stream()
// .map(SubmissionResponseDTO::fromEntity)
// .collect(Collectors.toList());
// }
}
Loading
Loading