Skip to content
Draft
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

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets just stick with MatchController for everything, and then we can handle the permissioning after the fact, once Randy's done.

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.patinanetwork.patchats.api.match;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.patinanetwork.patchats.api.match.dto.match.AdminMatchResponse;
import org.patinanetwork.patchats.api.match.dto.match.CreateMatchRequest;
import org.patinanetwork.patchats.common.dto.ApiResponder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/admin/matches")
@Tag(name = "Admin Matches")
@RequiredArgsConstructor
public class AdminMatchController {

private final MatchService matchService;

@Operation(summary = "Create a match between two members for a match cycle")
@PostMapping
public ResponseEntity<ApiResponder<AdminMatchResponse>> createMatch(
@Valid @RequestBody final CreateMatchRequest request) {
final AdminMatchResponse response = matchService.createMatch(request);
return ResponseEntity.ok(ApiResponder.success("Match created successfully", response));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.patinanetwork.patchats.api.match;

import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.patinanetwork.patchats.api.match.db.models.Match;
import org.patinanetwork.patchats.api.match.db.models.MatchCycle;
import org.patinanetwork.patchats.api.match.db.repos.MatchCycleRepo;
import org.patinanetwork.patchats.api.match.db.repos.MatchRepo;
import org.patinanetwork.patchats.api.match.dto.match.AdminMatchResponse;
import org.patinanetwork.patchats.api.match.dto.match.CreateMatchRequest;
import org.patinanetwork.patchats.common.web.exception.MatchCycleNotFoundException;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class MatchService {

private static final String DEFAULT_MATCH_STATUS = "PENDING";
private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");

private final MatchRepo matchRepo;
private final MatchCycleRepo matchCycleRepo;

public AdminMatchResponse createMatch(CreateMatchRequest request) {
MatchCycle cycle = matchCycleRepo
.getMatchCycleById(request.matchCycleId())
.orElseThrow(() -> new MatchCycleNotFoundException(request.matchCycleId()));

Match match = Match.builder()
.id(UUID.randomUUID())
.memberAId(request.memberAId())
.memberBId(request.memberBId())
.matchCycleId(request.matchCycleId())
.matchScore(request.matchScore())
.status(request.status() == null ? DEFAULT_MATCH_STATUS : request.status())
.build();

Match createdMatch = matchRepo.createMatch(match);
return AdminMatchResponse.from(createdMatch, deriveMonth(cycle));
}

/** Derives the "YYYY-MM" month label for a match from its cycle's run time (UTC). */
private String deriveMonth(final MatchCycle cycle) {
return MONTH_FORMATTER.format(cycle.getRunAt().atZone(ZoneOffset.UTC));
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep requests and response names synced, even if you have to repeat them.

I know it's kinda annoying and verbose, but it should be ->
CreateMatchCycleRequest
CreateMatchCycleResponse

I personally like having protobuf files to condense all of these DTOs into one file, but that's more infra to set up.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.patinanetwork.patchats.api.match.dto.cycle;

import jakarta.validation.constraints.NotNull;
import java.time.Instant;

public record CreateMatchCycleRequest(
@NotNull Instant runAt, String period, Integer totalMembers, Integer totalMatched) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.patinanetwork.patchats.api.match.dto.cycle;

public record MatchCycleDetailQuery(String memberIndustry, String status) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.patinanetwork.patchats.api.match.dto.cycle;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.patinanetwork.patchats.api.match.dto.match.AdminMatchResponse;

@Getter
@Builder
@ToString
@EqualsAndHashCode
public class MatchCycleDetailResponse {

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final MatchCycleResponse cycle;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final List<AdminMatchResponse> matches;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.patinanetwork.patchats.api.match.dto.cycle;

import java.time.Instant;

public record MatchCycleListQuery(String period, Instant startTime, Instant endTime) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.patinanetwork.patchats.api.match.dto.cycle;

import io.swagger.v3.oas.annotations.media.Schema;
import java.time.Instant;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.patinanetwork.patchats.api.match.db.models.MatchCycle;

@Getter
@Builder
@ToString
@EqualsAndHashCode
public class MatchCycleResponse {

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final Integer matchCycleId;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED, nullable = true)
private final String period;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final Instant runAt;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final Integer totalMembers;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final Integer totalMatched;

public static MatchCycleResponse from(final MatchCycle cycle) {
return MatchCycleResponse.builder()
.matchCycleId(cycle.getId())
.period(cycle.getPeriod())
.runAt(cycle.getRunAt())
.totalMembers(cycle.getTotalMembers())
.totalMatched(cycle.getTotalMatched())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.patinanetwork.patchats.api.match.dto.cycle;

import java.time.Instant;

public record UpdateMatchCycleRequest(Instant runAt, String period, Integer totalMembers, Integer totalMatched) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.patinanetwork.patchats.api.match.dto.match;

import io.swagger.v3.oas.annotations.media.Schema;
import java.time.Instant;
import java.util.UUID;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.patinanetwork.patchats.api.match.db.models.Match;

@Getter
@Builder
@ToString
@EqualsAndHashCode
public class AdminMatchResponse {

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final UUID matchId;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final UUID memberAId;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final UUID memberBId;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final Integer matchCycleId;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final String month;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final String status;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED, nullable = true)
private final Double matchScore;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED, nullable = true)
private final String feedbackA;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED, nullable = true)
private final String feedbackB;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final Instant createdAt;

public static AdminMatchResponse from(final Match match, final String month) {
return AdminMatchResponse.builder()
.matchId(match.getId())
.memberAId(match.getMemberAId())
.memberBId(match.getMemberBId())
.matchCycleId(match.getMatchCycleId())
.month(month)
.status(match.getStatus())
.matchScore(match.getMatchScore())
.feedbackA(match.getFeedbackA())
.feedbackB(match.getFeedbackB())
.createdAt(match.getCreatedAt())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.patinanetwork.patchats.api.match.dto.match;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import java.util.List;

public record BulkCreateMatchesRequest(@Valid @NotEmpty List<CreateMatchRequest> matches) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.patinanetwork.patchats.api.match.dto.match;

import jakarta.validation.constraints.NotNull;
import java.util.UUID;

public record CreateMatchRequest(
@NotNull UUID memberAId,
@NotNull UUID memberBId,
@NotNull Integer matchCycleId,
Double matchScore,
String status) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.patinanetwork.patchats.api.match.dto.match;

import java.time.Instant;
import java.util.UUID;

public record MatchListQuery(UUID memberId, String period, Instant startTime, Instant endTime, String status) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.patinanetwork.patchats.api.match.dto.match;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.UUID;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.patinanetwork.patchats.api.match.db.models.Match;

@Getter
@Builder
@ToString
@EqualsAndHashCode
public class MatchResponse {

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final UUID matchId;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final UUID memberAId;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final UUID memberBId;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final String month;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final String status;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED, nullable = true)
private final String feedbackA;

@Schema(requiredMode = Schema.RequiredMode.REQUIRED, nullable = true)
private final String feedbackB;

public static MatchResponse from(final Match match, final String month) {
return MatchResponse.builder()
.matchId(match.getId())
.memberAId(match.getMemberAId())
.memberBId(match.getMemberBId())
.month(month)
.status(match.getStatus())
.feedbackA(match.getFeedbackA())
.feedbackB(match.getFeedbackB())
.build();
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets drop everything related to feedback.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.patinanetwork.patchats.api.match.dto.match;

import jakarta.validation.constraints.NotBlank;

public record UpdateMatchFeedbackRequest(@NotBlank String feedback) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.patinanetwork.patchats.api.match.dto.match;

import jakarta.validation.constraints.NotBlank;

public record UpdateMatchStatusRequest(@NotBlank String status) {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.util.stream.Collectors;
import org.patinanetwork.patchats.common.dto.ApiResponder;
import org.patinanetwork.patchats.common.web.exception.MatchCycleNotFoundException;
import org.patinanetwork.patchats.common.web.exception.MatchNotFoundException;
import org.patinanetwork.patchats.common.web.exception.MemberDuplicateException;
import org.patinanetwork.patchats.common.web.exception.MemberNotFoundException;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -34,6 +36,16 @@ public ResponseEntity<ApiResponder<Void>> handleMemberDuplicate(final MemberDupl
return ResponseEntity.status(HttpStatus.CONFLICT).body(ApiResponder.failure(ex.getMessage()));
}

@ExceptionHandler(MatchNotFoundException.class)
public ResponseEntity<ApiResponder<Void>> handleMatchNotFound(final MatchNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ApiResponder.failure(ex.getMessage()));
}

@ExceptionHandler(MatchCycleNotFoundException.class)
public ResponseEntity<ApiResponder<Void>> handleMatchCycleNotFound(final MatchCycleNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ApiResponder.failure(ex.getMessage()));
}

private String formatError(final FieldError error) {
return error.getField() + " " + error.getDefaultMessage();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.patinanetwork.patchats.common.web.exception;

public class MatchCycleNotFoundException extends RuntimeException {
public MatchCycleNotFoundException(Integer id) {
super("Match Cycle with ID " + id + " not found");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.patinanetwork.patchats.common.web.exception;

import java.util.UUID;

public class MatchNotFoundException extends RuntimeException {
public MatchNotFoundException(UUID id) {
super("Match with ID " + id + " not found");
}
}
Loading