Added endpoints and logic for user following system.

This commit is contained in:
Andrew Lalis 2023-02-06 10:41:46 +01:00
parent e69008a582
commit 6097b0df7e
5 changed files with 91 additions and 12 deletions

View File

@ -1,12 +1,10 @@
package nl.andrewlalis.gymboard_api.domains.auth.controller;
import nl.andrewlalis.gymboard_api.domains.auth.dto.*;
import nl.andrewlalis.gymboard_api.domains.auth.model.User;
import nl.andrewlalis.gymboard_api.domains.auth.service.TokenService;
import nl.andrewlalis.gymboard_api.domains.auth.service.UserService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
@RestController

View File

@ -3,12 +3,11 @@ package nl.andrewlalis.gymboard_api.domains.auth.controller;
import nl.andrewlalis.gymboard_api.domains.auth.dto.*;
import nl.andrewlalis.gymboard_api.domains.auth.model.User;
import nl.andrewlalis.gymboard_api.domains.auth.service.UserService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
@RestController
public class UserController {
@ -66,4 +65,26 @@ public class UserController {
) {
return userService.updatePreferences(user.getId(), payload);
}
@PostMapping(path = "/auth/users/{userId}/followers")
public ResponseEntity<Void> followUser(@AuthenticationPrincipal User myUser, @PathVariable String userId) {
userService.followUser(myUser.getId(), userId);
return ResponseEntity.ok().build();
}
@DeleteMapping(path = "/auth/users/{userId}/followers")
public ResponseEntity<Void> unfollowUser(@AuthenticationPrincipal User myUser, @PathVariable String userId) {
userService.unfollowUser(myUser.getId(), userId);
return ResponseEntity.ok().build();
}
@GetMapping(path = "/auth/me/followers")
public Page<UserResponse> getFollowers(@AuthenticationPrincipal User user, Pageable pageable) {
return userService.getFollowers(user.getId(), pageable);
}
@GetMapping(path = "/auth/me/following")
public Page<UserResponse> getFollowing(@AuthenticationPrincipal User user, Pageable pageable) {
return userService.getFollowing(user.getId(), pageable);
}
}

View File

@ -0,0 +1,20 @@
package nl.andrewlalis.gymboard_api.domains.auth.dao;
import nl.andrewlalis.gymboard_api.domains.auth.model.User;
import nl.andrewlalis.gymboard_api.domains.auth.model.UserFollowing;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Repository;
@Repository
public interface UserFollowingRepository extends JpaRepository<UserFollowing, Long> {
boolean existsByFollowedUserAndFollowingUser(User followedUser, User followingUser);
@Modifying
void deleteByFollowedUserAndFollowingUser(User followedUser, User followingUser);
Page<UserFollowing> findAllByFollowedUserOrderByCreatedAtDesc(User followedUser, Pageable pageable);
Page<UserFollowing> findAllByFollowingUserOrderByCreatedAtDesc(User followingUser, Pageable pageable);
}

View File

@ -10,7 +10,10 @@ import java.time.LocalDateTime;
* have impacts on the notifications that a user can receive.
*/
@Entity
@Table(name = "auth_user_following")
@Table(
name = "auth_user_following",
uniqueConstraints = @UniqueConstraint(columnNames = {"followed_user_id", "following_user_id"})
)
public class UserFollowing {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

View File

@ -5,15 +5,14 @@ import jakarta.mail.internet.MimeMessage;
import nl.andrewlalis.gymboard_api.domains.api.model.WeightUnit;
import nl.andrewlalis.gymboard_api.domains.auth.dao.*;
import nl.andrewlalis.gymboard_api.domains.auth.dto.*;
import nl.andrewlalis.gymboard_api.domains.auth.model.PasswordResetCode;
import nl.andrewlalis.gymboard_api.domains.auth.model.User;
import nl.andrewlalis.gymboard_api.domains.auth.model.UserActivationCode;
import nl.andrewlalis.gymboard_api.domains.auth.model.UserPersonalDetails;
import nl.andrewlalis.gymboard_api.domains.auth.model.*;
import nl.andrewlalis.gymboard_api.util.StringGenerator;
import nl.andrewlalis.gymboard_api.util.ULID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
@ -38,6 +37,7 @@ public class UserService {
private final UserPreferencesRepository userPreferencesRepository;
private final UserActivationCodeRepository activationCodeRepository;
private final PasswordResetCodeRepository passwordResetCodeRepository;
private final UserFollowingRepository userFollowingRepository;
private final ULID ulid;
private final PasswordEncoder passwordEncoder;
private final JavaMailSender mailSender;
@ -51,7 +51,7 @@ public class UserService {
UserPreferencesRepository userPreferencesRepository,
UserActivationCodeRepository activationCodeRepository,
PasswordResetCodeRepository passwordResetCodeRepository,
ULID ulid,
UserFollowingRepository userFollowingRepository, ULID ulid,
PasswordEncoder passwordEncoder,
JavaMailSender mailSender
) {
@ -60,6 +60,7 @@ public class UserService {
this.userPreferencesRepository = userPreferencesRepository;
this.activationCodeRepository = activationCodeRepository;
this.passwordResetCodeRepository = passwordResetCodeRepository;
this.userFollowingRepository = userFollowingRepository;
this.ulid = ulid;
this.passwordEncoder = passwordEncoder;
this.mailSender = mailSender;
@ -276,4 +277,40 @@ public class UserService {
p = userPreferencesRepository.save(p);
return new UserPreferencesResponse(p);
}
@Transactional
public void followUser(String followerId, String followedId) {
User follower = userRepository.findById(followerId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
User followed = userRepository.findById(followedId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
if (!userFollowingRepository.existsByFollowedUserAndFollowingUser(followed, follower)) {
userFollowingRepository.save(new UserFollowing(followed, follower));
}
}
@Transactional
public void unfollowUser(String followerId, String followedId) {
User follower = userRepository.findById(followerId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
User followed = userRepository.findById(followedId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
userFollowingRepository.deleteByFollowedUserAndFollowingUser(followed, follower);
}
@Transactional(readOnly = true)
public Page<UserResponse> getFollowers(String userId, Pageable pageable) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
return userFollowingRepository.findAllByFollowedUserOrderByCreatedAtDesc(user, pageable)
.map(UserFollowing::getFollowingUser)
.map(UserResponse::new);
}
@Transactional(readOnly = true)
public Page<UserResponse> getFollowing(String userId, Pageable pageable) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
return userFollowingRepository.findAllByFollowingUserOrderByCreatedAtDesc(user, pageable)
.map(UserFollowing::getFollowedUser)
.map(UserResponse::new);
}
}