From aa843227d28305bc09690e7a669f15487ecd31ea Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Wed, 8 Feb 2023 08:30:16 +0100 Subject: [PATCH] Added report and voting entities, and api status controller. --- .../gymboard_api/StatusController.java | 15 +++++ .../gymboard_api/config/SecurityConfig.java | 5 +- .../domains/api/controller/GymController.java | 10 +-- .../api/controller/LeaderboardController.java | 4 +- .../api/controller/SubmissionController.java | 4 +- .../{exercise => }/ExerciseRepository.java | 4 +- .../ExerciseSubmissionRepository.java | 10 --- .../dao/submission/SubmissionRepository.java | 10 +++ .../domains/api/dto/ExerciseResponse.java | 2 +- ...ionPayload.java => SubmissionPayload.java} | 5 +- ...nResponse.java => SubmissionResponse.java} | 6 +- .../api/model/{exercise => }/Exercise.java | 2 +- .../Submission.java} | 12 ++-- .../model/submission/SubmissionReport.java | 63 +++++++++++++++++++ .../api/model/submission/SubmissionVote.java | 40 ++++++++++++ .../domains/api/service/ExerciseService.java | 2 +- .../domains/api/service/GymService.java | 12 ++-- .../api/service/LeaderboardService.java | 16 ++--- .../submission/ExerciseSubmissionService.java | 31 +++++---- .../domains/auth/model/UserReport.java | 60 ++++++++++++++++++ .../sample_data/SampleExerciseGenerator.java | 4 +- .../SampleSubmissionGenerator.java | 14 ++--- gymboard-app/src/api/main/auth.ts | 22 +++++-- .../components/ExerciseSubmissionListItem.vue | 2 +- gymboard-app/src/i18n/en-US/index.ts | 6 ++ gymboard-app/src/pages/SubmissionPage.vue | 2 +- gymboard-app/src/pages/UserPage.vue | 20 +++++- gymboard-app/src/stores/example-store.ts | 15 ----- 28 files changed, 300 insertions(+), 98 deletions(-) create mode 100644 gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/StatusController.java rename gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/{exercise => }/ExerciseRepository.java (60%) delete mode 100644 gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/exercise/ExerciseSubmissionRepository.java create mode 100644 gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/submission/SubmissionRepository.java rename gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/{ExerciseSubmissionPayload.java => SubmissionPayload.java} (62%) rename gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/{ExerciseSubmissionResponse.java => SubmissionResponse.java} (82%) rename gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/{exercise => }/Exercise.java (91%) rename gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/{exercise/ExerciseSubmission.java => submission/Submission.java} (85%) create mode 100644 gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/SubmissionReport.java create mode 100644 gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/SubmissionVote.java create mode 100644 gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/model/UserReport.java delete mode 100644 gymboard-app/src/stores/example-store.ts diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/StatusController.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/StatusController.java new file mode 100644 index 0000000..7257656 --- /dev/null +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/StatusController.java @@ -0,0 +1,15 @@ +package nl.andrewlalis.gymboard_api; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +@RestController +public class StatusController { + @GetMapping(path = "/status") + public ResponseEntity getServiceStatus() { + return ResponseEntity.ok(Map.of("online", true)); + } +} diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/SecurityConfig.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/SecurityConfig.java index a9cffbd..86a7801 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/SecurityConfig.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/config/SecurityConfig.java @@ -47,7 +47,10 @@ public class SecurityConfig { "/leaderboards", "/gyms/**", "/submissions/**", - "/auth/reset-password" + "/auth/reset-password", + "/auth/users/*", + "/auth/users/*/followers", + "/auth/users/*/following" ).permitAll() .requestMatchers(// Allow the following POST endpoints to be public. HttpMethod.POST, diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/GymController.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/GymController.java index b23b722..f368a70 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/GymController.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/GymController.java @@ -1,8 +1,8 @@ package nl.andrewlalis.gymboard_api.domains.api.controller; import nl.andrewlalis.gymboard_api.domains.api.dto.CompoundGymId; -import nl.andrewlalis.gymboard_api.domains.api.dto.ExerciseSubmissionPayload; -import nl.andrewlalis.gymboard_api.domains.api.dto.ExerciseSubmissionResponse; +import nl.andrewlalis.gymboard_api.domains.api.dto.SubmissionPayload; +import nl.andrewlalis.gymboard_api.domains.api.dto.SubmissionResponse; import nl.andrewlalis.gymboard_api.domains.api.dto.GymResponse; import nl.andrewlalis.gymboard_api.domains.api.service.GymService; import nl.andrewlalis.gymboard_api.domains.api.service.submission.ExerciseSubmissionService; @@ -32,15 +32,15 @@ public class GymController { } @GetMapping(path = "/recent-submissions") - public List getRecentSubmissions(@PathVariable String compoundId) { + public List getRecentSubmissions(@PathVariable String compoundId) { return gymService.getRecentSubmissions(CompoundGymId.parse(compoundId)); } @PostMapping(path = "/submissions") - public ExerciseSubmissionResponse createSubmission( + public SubmissionResponse createSubmission( @PathVariable String compoundId, @AuthenticationPrincipal User user, - @RequestBody ExerciseSubmissionPayload payload + @RequestBody SubmissionPayload payload ) { return submissionService.createSubmission(CompoundGymId.parse(compoundId), user.getId(), payload); } diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/LeaderboardController.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/LeaderboardController.java index a9d7336..934c50d 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/LeaderboardController.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/LeaderboardController.java @@ -1,6 +1,6 @@ package nl.andrewlalis.gymboard_api.domains.api.controller; -import nl.andrewlalis.gymboard_api.domains.api.dto.ExerciseSubmissionResponse; +import nl.andrewlalis.gymboard_api.domains.api.dto.SubmissionResponse; import nl.andrewlalis.gymboard_api.domains.api.service.LeaderboardService; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -21,7 +21,7 @@ public class LeaderboardController { } @GetMapping - public Page getLeaderboard( + public Page getLeaderboard( @RequestParam(name = "exercise") Optional exerciseShortName, @RequestParam(name = "gyms") Optional gymCompoundIdsString, @RequestParam(name = "t") Optional timeframe, diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/SubmissionController.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/SubmissionController.java index 5c496e3..c900102 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/SubmissionController.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/SubmissionController.java @@ -1,6 +1,6 @@ package nl.andrewlalis.gymboard_api.domains.api.controller; -import nl.andrewlalis.gymboard_api.domains.api.dto.ExerciseSubmissionResponse; +import nl.andrewlalis.gymboard_api.domains.api.dto.SubmissionResponse; import nl.andrewlalis.gymboard_api.domains.api.service.submission.ExerciseSubmissionService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -17,7 +17,7 @@ public class SubmissionController { } @GetMapping(path = "/{submissionId}") - public ExerciseSubmissionResponse getSubmission(@PathVariable String submissionId) { + public SubmissionResponse getSubmission(@PathVariable String submissionId) { return submissionService.getSubmission(submissionId); } } diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/exercise/ExerciseRepository.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/ExerciseRepository.java similarity index 60% rename from gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/exercise/ExerciseRepository.java rename to gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/ExerciseRepository.java index cdf3bd7..6d20915 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/exercise/ExerciseRepository.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/ExerciseRepository.java @@ -1,6 +1,6 @@ -package nl.andrewlalis.gymboard_api.domains.api.dao.exercise; +package nl.andrewlalis.gymboard_api.domains.api.dao; -import nl.andrewlalis.gymboard_api.domains.api.model.exercise.Exercise; +import nl.andrewlalis.gymboard_api.domains.api.model.Exercise; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/exercise/ExerciseSubmissionRepository.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/exercise/ExerciseSubmissionRepository.java deleted file mode 100644 index cd978c5..0000000 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/exercise/ExerciseSubmissionRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package nl.andrewlalis.gymboard_api.domains.api.dao.exercise; - -import nl.andrewlalis.gymboard_api.domains.api.model.exercise.ExerciseSubmission; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -import org.springframework.stereotype.Repository; - -@Repository -public interface ExerciseSubmissionRepository extends JpaRepository, JpaSpecificationExecutor { -} diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/submission/SubmissionRepository.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/submission/SubmissionRepository.java new file mode 100644 index 0000000..0ce206f --- /dev/null +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dao/submission/SubmissionRepository.java @@ -0,0 +1,10 @@ +package nl.andrewlalis.gymboard_api.domains.api.dao.submission; + +import nl.andrewlalis.gymboard_api.domains.api.model.submission.Submission; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.stereotype.Repository; + +@Repository +public interface SubmissionRepository extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseResponse.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseResponse.java index 59007d9..01a742c 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseResponse.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseResponse.java @@ -1,6 +1,6 @@ package nl.andrewlalis.gymboard_api.domains.api.dto; -import nl.andrewlalis.gymboard_api.domains.api.model.exercise.Exercise; +import nl.andrewlalis.gymboard_api.domains.api.model.Exercise; public record ExerciseResponse( String shortName, diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseSubmissionPayload.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/SubmissionPayload.java similarity index 62% rename from gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseSubmissionPayload.java rename to gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/SubmissionPayload.java index 5fffa6e..1fea3c6 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseSubmissionPayload.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/SubmissionPayload.java @@ -1,7 +1,10 @@ package nl.andrewlalis.gymboard_api.domains.api.dto; -public record ExerciseSubmissionPayload( +import java.time.LocalDateTime; + +public record SubmissionPayload( String exerciseShortName, + LocalDateTime performedAt, float weight, String weightUnit, int reps, diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseSubmissionResponse.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/SubmissionResponse.java similarity index 82% rename from gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseSubmissionResponse.java rename to gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/SubmissionResponse.java index 897a5b5..edc2ed0 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/ExerciseSubmissionResponse.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/SubmissionResponse.java @@ -1,10 +1,10 @@ package nl.andrewlalis.gymboard_api.domains.api.dto; -import nl.andrewlalis.gymboard_api.domains.api.model.exercise.ExerciseSubmission; +import nl.andrewlalis.gymboard_api.domains.api.model.submission.Submission; import nl.andrewlalis.gymboard_api.domains.auth.dto.UserResponse; import nl.andrewlalis.gymboard_api.util.StandardDateFormatter; -public record ExerciseSubmissionResponse( +public record SubmissionResponse( String id, String createdAt, GymSimpleResponse gym, @@ -17,7 +17,7 @@ public record ExerciseSubmissionResponse( double metricWeight, int reps ) { - public ExerciseSubmissionResponse(ExerciseSubmission submission) { + public SubmissionResponse(Submission submission) { this( submission.getId(), StandardDateFormatter.format(submission.getCreatedAt()), diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/exercise/Exercise.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/Exercise.java similarity index 91% rename from gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/exercise/Exercise.java rename to gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/Exercise.java index baaf698..37ddafd 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/exercise/Exercise.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/Exercise.java @@ -1,4 +1,4 @@ -package nl.andrewlalis.gymboard_api.domains.api.model.exercise; +package nl.andrewlalis.gymboard_api.domains.api.model; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/exercise/ExerciseSubmission.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/Submission.java similarity index 85% rename from gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/exercise/ExerciseSubmission.java rename to gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/Submission.java index 57ba2f8..dd0901d 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/exercise/ExerciseSubmission.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/Submission.java @@ -1,18 +1,18 @@ -package nl.andrewlalis.gymboard_api.domains.api.model.exercise; +package nl.andrewlalis.gymboard_api.domains.api.model.submission; import jakarta.persistence.*; +import nl.andrewlalis.gymboard_api.domains.api.model.Exercise; import nl.andrewlalis.gymboard_api.domains.api.model.Gym; import nl.andrewlalis.gymboard_api.domains.api.model.WeightUnit; import nl.andrewlalis.gymboard_api.domains.auth.model.User; import org.hibernate.annotations.CreationTimestamp; import java.math.BigDecimal; -import java.time.LocalDate; import java.time.LocalDateTime; @Entity -@Table(name = "exercise_submission") -public class ExerciseSubmission { +@Table(name = "submission") +public class Submission { @Id @Column(nullable = false, updatable = false, length = 26) private String id; @@ -53,9 +53,9 @@ public class ExerciseSubmission { @Column(nullable = false) private int reps; - public ExerciseSubmission() {} + public Submission() {} - public ExerciseSubmission(String id, Gym gym, Exercise exercise, User user, LocalDateTime performedAt, String videoFileId, BigDecimal rawWeight, WeightUnit unit, BigDecimal metricWeight, int reps) { + public Submission(String id, Gym gym, Exercise exercise, User user, LocalDateTime performedAt, String videoFileId, BigDecimal rawWeight, WeightUnit unit, BigDecimal metricWeight, int reps) { this.id = id; this.gym = gym; this.exercise = exercise; diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/SubmissionReport.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/SubmissionReport.java new file mode 100644 index 0000000..20cf0a0 --- /dev/null +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/SubmissionReport.java @@ -0,0 +1,63 @@ +package nl.andrewlalis.gymboard_api.domains.api.model.submission; + +import jakarta.persistence.*; +import nl.andrewlalis.gymboard_api.domains.auth.model.User; +import org.hibernate.annotations.CreationTimestamp; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "submission_report") +public class SubmissionReport { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @CreationTimestamp + private LocalDateTime createdAt; + + @ManyToOne(optional = false, fetch = FetchType.LAZY) + private Submission submission; + + @ManyToOne(fetch = FetchType.LAZY) + private User user; + + @Column(nullable = false) + private String reason; + + @Column(length = 1024) + private String description; + + public SubmissionReport() {} + + public SubmissionReport(Submission submission, User user, String reason, String description) { + this.submission = submission; + this.user = user; + this.reason = reason; + this.description = description; + } + + public Long getId() { + return id; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public Submission getSubmission() { + return submission; + } + + public User getUser() { + return user; + } + + public String getReason() { + return reason; + } + + public String getDescription() { + return description; + } +} diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/SubmissionVote.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/SubmissionVote.java new file mode 100644 index 0000000..171662e --- /dev/null +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/SubmissionVote.java @@ -0,0 +1,40 @@ +package nl.andrewlalis.gymboard_api.domains.api.model.submission; + +import jakarta.persistence.*; +import nl.andrewlalis.gymboard_api.domains.auth.model.User; + +@Entity +@Table( + name = "submission_vote", + uniqueConstraints = @UniqueConstraint(columnNames = {"submission_id", "user_id"}) +) +public class SubmissionVote { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(optional = false, fetch = FetchType.LAZY) + private Submission submission; + + @ManyToOne(optional = false, fetch = FetchType.LAZY) + private User user; + + public SubmissionVote() {} + + public SubmissionVote(Submission submission, User user) { + this.submission = submission; + this.user = user; + } + + public Long getId() { + return id; + } + + public Submission getSubmission() { + return submission; + } + + public User getUser() { + return user; + } +} diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/ExerciseService.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/ExerciseService.java index 9312228..5f214d0 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/ExerciseService.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/ExerciseService.java @@ -1,7 +1,7 @@ package nl.andrewlalis.gymboard_api.domains.api.service; import nl.andrewlalis.gymboard_api.domains.api.dto.ExerciseResponse; -import nl.andrewlalis.gymboard_api.domains.api.dao.exercise.ExerciseRepository; +import nl.andrewlalis.gymboard_api.domains.api.dao.ExerciseRepository; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/GymService.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/GymService.java index dde78f1..6ec8c7f 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/GymService.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/GymService.java @@ -1,10 +1,10 @@ package nl.andrewlalis.gymboard_api.domains.api.service; import nl.andrewlalis.gymboard_api.domains.api.dto.CompoundGymId; -import nl.andrewlalis.gymboard_api.domains.api.dto.ExerciseSubmissionResponse; +import nl.andrewlalis.gymboard_api.domains.api.dto.SubmissionResponse; import nl.andrewlalis.gymboard_api.domains.api.dto.GymResponse; import nl.andrewlalis.gymboard_api.domains.api.dao.GymRepository; -import nl.andrewlalis.gymboard_api.domains.api.dao.exercise.ExerciseSubmissionRepository; +import nl.andrewlalis.gymboard_api.domains.api.dao.submission.SubmissionRepository; import nl.andrewlalis.gymboard_api.domains.api.model.Gym; import nl.andrewlalis.gymboard_api.util.PredicateBuilder; import org.slf4j.Logger; @@ -22,9 +22,9 @@ public class GymService { private static final Logger log = LoggerFactory.getLogger(GymService.class); private final GymRepository gymRepository; - private final ExerciseSubmissionRepository submissionRepository; + private final SubmissionRepository submissionRepository; - public GymService(GymRepository gymRepository, ExerciseSubmissionRepository submissionRepository) { + public GymService(GymRepository gymRepository, SubmissionRepository submissionRepository) { this.gymRepository = gymRepository; this.submissionRepository = submissionRepository; } @@ -37,7 +37,7 @@ public class GymService { } @Transactional(readOnly = true) - public List getRecentSubmissions(CompoundGymId id) { + public List getRecentSubmissions(CompoundGymId id) { Gym gym = gymRepository.findByCompoundId(id) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); return submissionRepository.findAll((root, query, criteriaBuilder) -> { @@ -49,7 +49,7 @@ public class GymService { .with(criteriaBuilder.equal(root.get("gym"), gym)) .build(); }, PageRequest.of(0, 10)) - .map(ExerciseSubmissionResponse::new) + .map(SubmissionResponse::new) .toList(); } } diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/LeaderboardService.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/LeaderboardService.java index db3cb0b..891d981 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/LeaderboardService.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/LeaderboardService.java @@ -1,13 +1,13 @@ package nl.andrewlalis.gymboard_api.domains.api.service; import nl.andrewlalis.gymboard_api.domains.api.dto.CompoundGymId; -import nl.andrewlalis.gymboard_api.domains.api.dto.ExerciseSubmissionResponse; +import nl.andrewlalis.gymboard_api.domains.api.dto.SubmissionResponse; import nl.andrewlalis.gymboard_api.domains.api.dao.GymRepository; -import nl.andrewlalis.gymboard_api.domains.api.dao.exercise.ExerciseRepository; -import nl.andrewlalis.gymboard_api.domains.api.dao.exercise.ExerciseSubmissionRepository; +import nl.andrewlalis.gymboard_api.domains.api.dao.ExerciseRepository; +import nl.andrewlalis.gymboard_api.domains.api.dao.submission.SubmissionRepository; import nl.andrewlalis.gymboard_api.domains.api.model.Gym; import nl.andrewlalis.gymboard_api.domains.api.model.LeaderboardTimeframe; -import nl.andrewlalis.gymboard_api.domains.api.model.exercise.Exercise; +import nl.andrewlalis.gymboard_api.domains.api.model.Exercise; import nl.andrewlalis.gymboard_api.util.PredicateBuilder; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -27,18 +27,18 @@ import java.util.Optional; */ @Service public class LeaderboardService { - private final ExerciseSubmissionRepository submissionRepository; + private final SubmissionRepository submissionRepository; private final ExerciseRepository exerciseRepository; private final GymRepository gymRepository; - public LeaderboardService(ExerciseSubmissionRepository submissionRepository, ExerciseRepository exerciseRepository, GymRepository gymRepository) { + public LeaderboardService(SubmissionRepository submissionRepository, ExerciseRepository exerciseRepository, GymRepository gymRepository) { this.submissionRepository = submissionRepository; this.exerciseRepository = exerciseRepository; this.gymRepository = gymRepository; } @Transactional(readOnly = true) - public Page getTopSubmissions( + public Page getTopSubmissions( Optional exerciseShortName, Optional gymCompoundIdsString, Optional optionalTimeframe, @@ -68,7 +68,7 @@ public class LeaderboardService { } return pb.build(); - }, pageable).map(ExerciseSubmissionResponse::new); + }, pageable).map(SubmissionResponse::new); } private List parseGymCompoundIdsString(String s) { diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/submission/ExerciseSubmissionService.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/submission/ExerciseSubmissionService.java index f3e6860..08681a6 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/submission/ExerciseSubmissionService.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/submission/ExerciseSubmissionService.java @@ -1,15 +1,15 @@ package nl.andrewlalis.gymboard_api.domains.api.service.submission; import nl.andrewlalis.gymboard_api.domains.api.dao.GymRepository; -import nl.andrewlalis.gymboard_api.domains.api.dao.exercise.ExerciseRepository; -import nl.andrewlalis.gymboard_api.domains.api.dao.exercise.ExerciseSubmissionRepository; +import nl.andrewlalis.gymboard_api.domains.api.dao.ExerciseRepository; +import nl.andrewlalis.gymboard_api.domains.api.dao.submission.SubmissionRepository; import nl.andrewlalis.gymboard_api.domains.api.dto.CompoundGymId; -import nl.andrewlalis.gymboard_api.domains.api.dto.ExerciseSubmissionPayload; -import nl.andrewlalis.gymboard_api.domains.api.dto.ExerciseSubmissionResponse; +import nl.andrewlalis.gymboard_api.domains.api.dto.SubmissionPayload; +import nl.andrewlalis.gymboard_api.domains.api.dto.SubmissionResponse; import nl.andrewlalis.gymboard_api.domains.api.model.Gym; import nl.andrewlalis.gymboard_api.domains.api.model.WeightUnit; -import nl.andrewlalis.gymboard_api.domains.api.model.exercise.Exercise; -import nl.andrewlalis.gymboard_api.domains.api.model.exercise.ExerciseSubmission; +import nl.andrewlalis.gymboard_api.domains.api.model.Exercise; +import nl.andrewlalis.gymboard_api.domains.api.model.submission.Submission; import nl.andrewlalis.gymboard_api.domains.auth.dao.UserRepository; import nl.andrewlalis.gymboard_api.domains.auth.model.User; import nl.andrewlalis.gymboard_api.util.ULID; @@ -21,7 +21,6 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.server.ResponseStatusException; import java.math.BigDecimal; -import java.time.LocalDate; import java.time.LocalDateTime; /** @@ -35,25 +34,25 @@ public class ExerciseSubmissionService { private final GymRepository gymRepository; private final UserRepository userRepository; private final ExerciseRepository exerciseRepository; - private final ExerciseSubmissionRepository exerciseSubmissionRepository; + private final SubmissionRepository submissionRepository; private final ULID ulid; public ExerciseSubmissionService(GymRepository gymRepository, UserRepository userRepository, ExerciseRepository exerciseRepository, - ExerciseSubmissionRepository exerciseSubmissionRepository, + SubmissionRepository submissionRepository, ULID ulid) { this.gymRepository = gymRepository; this.userRepository = userRepository; this.exerciseRepository = exerciseRepository; - this.exerciseSubmissionRepository = exerciseSubmissionRepository; + this.submissionRepository = submissionRepository; this.ulid = ulid; } @Transactional(readOnly = true) - public ExerciseSubmissionResponse getSubmission(String submissionId) { - ExerciseSubmission submission = exerciseSubmissionRepository.findById(submissionId) + public SubmissionResponse getSubmission(String submissionId) { + Submission submission = submissionRepository.findById(submissionId) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - return new ExerciseSubmissionResponse(submission); + return new SubmissionResponse(submission); } /** @@ -64,7 +63,7 @@ public class ExerciseSubmissionService { * @return The saved submission. */ @Transactional - public ExerciseSubmissionResponse createSubmission(CompoundGymId id, String userId, ExerciseSubmissionPayload payload) { + public SubmissionResponse createSubmission(CompoundGymId id, String userId, SubmissionPayload payload) { User user = userRepository.findById(userId) .orElseThrow(() -> new ResponseStatusException(HttpStatus.FORBIDDEN)); Gym gym = gymRepository.findByCompoundId(id) @@ -81,7 +80,7 @@ public class ExerciseSubmissionService { if (weightUnit == WeightUnit.POUNDS) { metricWeight = WeightUnit.toKilograms(rawWeight); } - ExerciseSubmission submission = exerciseSubmissionRepository.saveAndFlush(new ExerciseSubmission( + Submission submission = submissionRepository.saveAndFlush(new Submission( ulid.nextULID(), gym, exercise, @@ -93,6 +92,6 @@ public class ExerciseSubmissionService { metricWeight, payload.reps() )); - return new ExerciseSubmissionResponse(submission); + return new SubmissionResponse(submission); } } diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/model/UserReport.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/model/UserReport.java new file mode 100644 index 0000000..5dae63b --- /dev/null +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/model/UserReport.java @@ -0,0 +1,60 @@ +package nl.andrewlalis.gymboard_api.domains.auth.model; + +import jakarta.persistence.*; +import org.hibernate.annotations.CreationTimestamp; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "auth_user_report") +public class UserReport { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @CreationTimestamp + private LocalDateTime createdAt; + + @ManyToOne(optional = false, fetch = FetchType.LAZY) + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + private User reportedBy; + + @Column(nullable = false) + private String reason; + + @Column(length = 1024) + private String description; + + public UserReport(User user, User reportedBy, String reason, String description) { + this.user = user; + this.reportedBy = reportedBy; + this.reason = reason; + this.description = description; + } + + public Long getId() { + return id; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public User getUser() { + return user; + } + + public User getReportedBy() { + return reportedBy; + } + + public String getReason() { + return reason; + } + + public String getDescription() { + return description; + } +} diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/sample_data/SampleExerciseGenerator.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/sample_data/SampleExerciseGenerator.java index 05a83a4..860b9db 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/sample_data/SampleExerciseGenerator.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/sample_data/SampleExerciseGenerator.java @@ -1,7 +1,7 @@ package nl.andrewlalis.gymboard_api.util.sample_data; -import nl.andrewlalis.gymboard_api.domains.api.dao.exercise.ExerciseRepository; -import nl.andrewlalis.gymboard_api.domains.api.model.exercise.Exercise; +import nl.andrewlalis.gymboard_api.domains.api.dao.ExerciseRepository; +import nl.andrewlalis.gymboard_api.domains.api.model.Exercise; import nl.andrewlalis.gymboard_api.util.CsvUtil; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/sample_data/SampleSubmissionGenerator.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/sample_data/SampleSubmissionGenerator.java index 33e34cc..ab53843 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/sample_data/SampleSubmissionGenerator.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/sample_data/SampleSubmissionGenerator.java @@ -1,12 +1,12 @@ package nl.andrewlalis.gymboard_api.util.sample_data; import nl.andrewlalis.gymboard_api.domains.api.dao.GymRepository; -import nl.andrewlalis.gymboard_api.domains.api.dao.exercise.ExerciseRepository; -import nl.andrewlalis.gymboard_api.domains.api.dao.exercise.ExerciseSubmissionRepository; +import nl.andrewlalis.gymboard_api.domains.api.dao.ExerciseRepository; +import nl.andrewlalis.gymboard_api.domains.api.dao.submission.SubmissionRepository; import nl.andrewlalis.gymboard_api.domains.api.model.Gym; import nl.andrewlalis.gymboard_api.domains.api.model.WeightUnit; -import nl.andrewlalis.gymboard_api.domains.api.model.exercise.Exercise; -import nl.andrewlalis.gymboard_api.domains.api.model.exercise.ExerciseSubmission; +import nl.andrewlalis.gymboard_api.domains.api.model.Exercise; +import nl.andrewlalis.gymboard_api.domains.api.model.submission.Submission; import nl.andrewlalis.gymboard_api.domains.api.service.cdn_client.CdnClient; import nl.andrewlalis.gymboard_api.domains.api.service.submission.ExerciseSubmissionService; import nl.andrewlalis.gymboard_api.domains.auth.dao.UserRepository; @@ -29,13 +29,13 @@ public class SampleSubmissionGenerator implements SampleDataGenerator { private final UserRepository userRepository; private final ExerciseRepository exerciseRepository; private final ExerciseSubmissionService submissionService; - private final ExerciseSubmissionRepository submissionRepository; + private final SubmissionRepository submissionRepository; private final ULID ulid; @Value("${app.cdn-origin}") private String cdnOrigin; - public SampleSubmissionGenerator(GymRepository gymRepository, UserRepository userRepository, ExerciseRepository exerciseRepository, ExerciseSubmissionService submissionService, ExerciseSubmissionRepository submissionRepository, ULID ulid) { + public SampleSubmissionGenerator(GymRepository gymRepository, UserRepository userRepository, ExerciseRepository exerciseRepository, ExerciseSubmissionService submissionService, SubmissionRepository submissionRepository, ULID ulid) { this.gymRepository = gymRepository; this.userRepository = userRepository; this.exerciseRepository = exerciseRepository; @@ -94,7 +94,7 @@ public class SampleSubmissionGenerator implements SampleDataGenerator { rawWeight = metricWeight.multiply(new BigDecimal("2.2046226218")); } - submissionRepository.save(new ExerciseSubmission( + submissionRepository.save(new Submission( ulid.nextULID(), randomChoice(gyms, random), randomChoice(exercises, random), diff --git a/gymboard-app/src/api/main/auth.ts b/gymboard-app/src/api/main/auth.ts index acb1beb..713542f 100644 --- a/gymboard-app/src/api/main/auth.ts +++ b/gymboard-app/src/api/main/auth.ts @@ -7,6 +7,8 @@ export interface User { activated: boolean; email: string; name: string; + personalDetails?: UserPersonalDetails; + preferences?: UserPreferences; } export enum PersonSex { @@ -20,6 +22,7 @@ export interface UserPersonalDetails { birthDate?: string; currentWeight?: number; currentWeightUnit?: number; + currentMetricWeight?: number; sex: PersonSex; } @@ -45,9 +48,18 @@ class AuthModule { private tokenRefreshTimer?: Timeout; + /** + * Attempts to use the given credentials to obtain an access token for + * sending authenticated requests. + * @param authStore The auth store to use to update app state. + * @param credentials The credentials for logging in. + */ public async login(authStore: AuthStoreType, credentials: TokenCredentials) { - authStore.token = await this.fetchNewToken(credentials); - authStore.user = await this.fetchMyUser(authStore); + authStore.token = await this.getNewToken(credentials); + authStore.user = await this.getMyUser(authStore); + // Load the user's attached data right away too. + authStore.user.personalDetails = await this.getMyPersonalDetails(authStore); + authStore.user.preferences = await this.getMyPreferences(authStore); clearTimeout(this.tokenRefreshTimer); this.tokenRefreshTimer = setTimeout( @@ -71,7 +83,7 @@ class AuthModule { return response.data; } - public async fetchNewToken(credentials: TokenCredentials): Promise { + public async getNewToken(credentials: TokenCredentials): Promise { const response = await api.post('/auth/token', credentials); return response.data.token; } @@ -81,12 +93,12 @@ class AuthModule { authStore.token = response.data.token; } - public async fetchMyUser(authStore: AuthStoreType): Promise { + public async getMyUser(authStore: AuthStoreType): Promise { const response = await api.get('/auth/me', authStore.axiosConfig); return response.data; } - public async fetchUser(userId: string, authStore: AuthStoreType): Promise { + public async getUser(userId: string, authStore: AuthStoreType): Promise { const response = await api.get(`/auth/users/${userId}`, authStore.axiosConfig); return response.data; } diff --git a/gymboard-app/src/components/ExerciseSubmissionListItem.vue b/gymboard-app/src/components/ExerciseSubmissionListItem.vue index a5efdfc..2b3b201 100644 --- a/gymboard-app/src/components/ExerciseSubmissionListItem.vue +++ b/gymboard-app/src/components/ExerciseSubmissionListItem.vue @@ -6,7 +6,7 @@ {{ submission.exercise.displayName }} - {{ submission.submitterName }} + {{ submission.user.name }} diff --git a/gymboard-app/src/i18n/en-US/index.ts b/gymboard-app/src/i18n/en-US/index.ts index a36ca18..6d39bac 100644 --- a/gymboard-app/src/i18n/en-US/index.ts +++ b/gymboard-app/src/i18n/en-US/index.ts @@ -39,6 +39,12 @@ export default { submit: 'Submit', }, }, + userPage: { + notFound: { + title: 'User Not Found', + description: 'We couldn\'t find the user you\'re looking for.' + } + }, accountMenuItem: { logIn: 'Login', myAccount: 'My Account', diff --git a/gymboard-app/src/pages/SubmissionPage.vue b/gymboard-app/src/pages/SubmissionPage.vue index 231f9f5..670af73 100644 --- a/gymboard-app/src/pages/SubmissionPage.vue +++ b/gymboard-app/src/pages/SubmissionPage.vue @@ -6,7 +6,7 @@ {{ submission.exercise.displayName }}

{{ submission.reps }} reps

-

by {{ submission.submitterName }}

+

by {{ submission.user.name }}

At {{ submission.gym.displayName }}

{{ submission.createdAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }} diff --git a/gymboard-app/src/pages/UserPage.vue b/gymboard-app/src/pages/UserPage.vue index 5f792fc..595c603 100644 --- a/gymboard-app/src/pages/UserPage.vue +++ b/gymboard-app/src/pages/UserPage.vue @@ -5,6 +5,10 @@

{{ user?.email }}

This is your account!

+ +

{{ $t('userPage.notFound.title') }}

+

{{ $t('userPage.notFound.description') }}

+
@@ -29,10 +33,22 @@ const user: Ref = ref(); */ const isOwnUser = ref(false); +/** + * Flag used to indicate whether we should show a "not found" message instead + * of the usual user page. + */ +const userNotFound = ref(false); + onMounted(async () => { const userId = route.params.userId as string; - user.value = await api.auth.fetchUser(userId, authStore); - isOwnUser.value = user.value.id === authStore.user?.id; + try { + user.value = await api.auth.getUser(userId, authStore); + } catch (error: any) { + if (error.response && error.response.code === 404) { + userNotFound.value = true; + } + } + isOwnUser.value = authStore.loggedIn && user.value.id === authStore.user?.id; }); diff --git a/gymboard-app/src/stores/example-store.ts b/gymboard-app/src/stores/example-store.ts deleted file mode 100644 index 83e8390..0000000 --- a/gymboard-app/src/stores/example-store.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { defineStore } from 'pinia'; - -export const useCounterStore = defineStore('counter', { - state: () => ({ - counter: 0, - }), - getters: { - doubleCount: (state) => state.counter * 2, - }, - actions: { - increment() { - this.counter++; - }, - }, -});