Added report and voting entities, and api status controller.

This commit is contained in:
Andrew Lalis 2023-02-08 08:30:16 +01:00
parent 080c44c467
commit aa843227d2
28 changed files with 300 additions and 98 deletions

View File

@ -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));
}
}

View File

@ -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,

View File

@ -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<ExerciseSubmissionResponse> getRecentSubmissions(@PathVariable String compoundId) {
public List<SubmissionResponse> 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);
}

View File

@ -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<ExerciseSubmissionResponse> getLeaderboard(
public Page<SubmissionResponse> getLeaderboard(
@RequestParam(name = "exercise") Optional<String> exerciseShortName,
@RequestParam(name = "gyms") Optional<String> gymCompoundIdsString,
@RequestParam(name = "t") Optional<String> timeframe,

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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<ExerciseSubmission, String>, JpaSpecificationExecutor<ExerciseSubmission> {
}

View File

@ -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<Submission, String>, JpaSpecificationExecutor<Submission> {
}

View File

@ -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,

View File

@ -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,

View File

@ -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()),

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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<ExerciseSubmissionResponse> getRecentSubmissions(CompoundGymId id) {
public List<SubmissionResponse> 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();
}
}

View File

@ -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<ExerciseSubmissionResponse> getTopSubmissions(
public Page<SubmissionResponse> getTopSubmissions(
Optional<String> exerciseShortName,
Optional<String> gymCompoundIdsString,
Optional<String> optionalTimeframe,
@ -68,7 +68,7 @@ public class LeaderboardService {
}
return pb.build();
}, pageable).map(ExerciseSubmissionResponse::new);
}, pageable).map(SubmissionResponse::new);
}
private List<Gym> parseGymCompoundIdsString(String s) {

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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),

View File

@ -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<string> {
public async getNewToken(credentials: TokenCredentials): Promise<string> {
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<User> {
public async getMyUser(authStore: AuthStoreType): Promise<User> {
const response = await api.get('/auth/me', authStore.axiosConfig);
return response.data;
}
public async fetchUser(userId: string, authStore: AuthStoreType): Promise<User> {
public async getUser(userId: string, authStore: AuthStoreType): Promise<User> {
const response = await api.get(`/auth/users/${userId}`, authStore.axiosConfig);
return response.data;
}

View File

@ -6,7 +6,7 @@
{{ submission.exercise.displayName }}
</q-item-label>
<q-item-label caption>
{{ submission.submitterName }}
{{ submission.user.name }}
</q-item-label>
</q-item-section>
<q-item-section side top>

View File

@ -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',

View File

@ -6,7 +6,7 @@
{{ submission.exercise.displayName }}
</h3>
<p>{{ submission.reps }} reps</p>
<p>by {{ submission.submitterName }}</p>
<p>by <router-link :to="'/users/' + submission.user.id">{{ submission.user.name }}</router-link></p>
<p>At <router-link :to="getGymRoute(submission.gym)">{{ submission.gym.displayName }}</router-link></p>
<p>
{{ submission.createdAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }}

View File

@ -5,6 +5,10 @@
<p>{{ user?.email }}</p>
<p v-if="isOwnUser">This is your account!</p>
</StandardCenteredPage>
<StandardCenteredPage v-if="userNotFound">
<h3>{{ $t('userPage.notFound.title') }}</h3>
<p>{{ $t('userPage.notFound.description') }}</p>
</StandardCenteredPage>
</q-page>
</template>
@ -29,10 +33,22 @@ const user: Ref<User | undefined> = 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;
});
</script>

View File

@ -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++;
},
},
});