27.1. Modify userId to be UUID instead of String (#27 refactor http calls to use userId instead of username or email)

This commit is contained in:
Art
2021-09-28 11:53:58 +03:00
parent 6a1a5301b7
commit 685fcf2136
9 changed files with 47 additions and 36 deletions

View File

@ -23,6 +23,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.validation.Valid; import javax.validation.Valid;
import java.io.IOException; import java.io.IOException;
import java.util.UUID;
import static org.springframework.http.HttpStatus.OK; import static org.springframework.http.HttpStatus.OK;
@ -93,7 +94,7 @@ public class UserResource {
@DeleteMapping("{userId}") @DeleteMapping("{userId}")
@PreAuthorize("hasAuthority('user:delete')") @PreAuthorize("hasAuthority('user:delete')")
public HttpResponse deleteUser(@PathVariable String userId) { public HttpResponse deleteUser(@PathVariable UUID userId) {
userService.deleteUser(userId); userService.deleteUser(userId);
return HttpResponse.builder() return HttpResponse.builder()
.httpStatusCode(OK.value()) .httpStatusCode(OK.value())
@ -114,12 +115,12 @@ public class UserResource {
} }
@GetMapping(path = "image/profile/{userId}/{filename}", produces = MediaType.IMAGE_JPEG_VALUE) @GetMapping(path = "image/profile/{userId}/{filename}", produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] getProfileImageByUserId(@PathVariable String userId, @PathVariable String filename) throws IOException { public byte[] getProfileImageByUserId(@PathVariable UUID userId, @PathVariable String filename) throws IOException {
return userService.getImageByUserId(userId, filename); return userService.getImageByUserId(userId, filename);
} }
@GetMapping(path = "image/profile/{userId}", produces = MediaType.IMAGE_JPEG_VALUE) @GetMapping(path = "image/profile/{userId}", produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] getDefaultProfileImage(@PathVariable String userId) { public byte[] getDefaultProfileImage(@PathVariable UUID userId) {
return userService.getDefaultProfileImage(userId); return userService.getDefaultProfileImage(userId);
} }

View File

@ -2,13 +2,12 @@ package net.shyshkin.study.fullstack.supportportal.backend.domain;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*; import lombok.*;
import org.hibernate.annotations.Type;
import javax.persistence.Entity; import javax.persistence.*;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.UUID;
@Entity @Entity
@ -27,7 +26,9 @@ public class User implements Serializable {
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY) @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private Long id; private Long id;
private String userId; @Type(type="org.hibernate.type.UUIDCharType")
@Column(length = 36, columnDefinition = "varchar(255)", updatable = false, nullable = false )
private UUID userId;
private String firstName; private String firstName;
private String lastName; private String lastName;
private String username; private String username;

View File

@ -4,6 +4,7 @@ import net.shyshkin.study.fullstack.supportportal.backend.domain.User;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
public interface UserRepository extends JpaRepository<User, Long> { public interface UserRepository extends JpaRepository<User, Long> {
@ -15,6 +16,6 @@ public interface UserRepository extends JpaRepository<User, Long> {
Boolean existsByEmail(String email); Boolean existsByEmail(String email);
Optional<User> findByUserId(String userId); Optional<User> findByUserId(UUID userId);
} }

View File

@ -8,6 +8,7 @@ import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.IOException;
import java.util.UUID;
public interface UserService extends UserDetailsService { public interface UserService extends UserDetailsService {
@ -23,7 +24,7 @@ public interface UserService extends UserDetailsService {
User updateUser(String username, UserDto userDto); User updateUser(String username, UserDto userDto);
void deleteUser(String userId); void deleteUser(UUID userId);
void resetPassword(String email); void resetPassword(String email);
@ -31,7 +32,7 @@ public interface UserService extends UserDetailsService {
byte[] getProfileImage(String username) throws IOException; byte[] getProfileImage(String username) throws IOException;
byte[] getImageByUserId(String userId, String filename) throws IOException; byte[] getImageByUserId(UUID userId, String filename) throws IOException;
byte[] getDefaultProfileImage(String userId); byte[] getDefaultProfileImage(UUID userId);
} }

View File

@ -102,17 +102,17 @@ public class UserServiceImpl implements UserService {
return addNewUser(newUserDto); return addNewUser(newUserDto);
} }
private String generateDefaultProfileImageUrl(String userId) { private String generateDefaultProfileImageUrl(UUID userId) {
return ServletUriComponentsBuilder.fromCurrentContextPath() return ServletUriComponentsBuilder.fromCurrentContextPath()
.path(DEFAULT_USER_IMAGE_PATH) .path(DEFAULT_USER_IMAGE_PATH)
.pathSegment(userId) .pathSegment(userId.toString())
.toUriString(); .toUriString();
} }
private String generateProfileImageUrl(String userId) { private String generateProfileImageUrl(UUID userId) {
return ServletUriComponentsBuilder.fromCurrentContextPath() return ServletUriComponentsBuilder.fromCurrentContextPath()
.path(DEFAULT_USER_IMAGE_PATH) .path(DEFAULT_USER_IMAGE_PATH)
.pathSegment(userId) .pathSegment(userId.toString())
.pathSegment(USER_IMAGE_FILENAME) .pathSegment(USER_IMAGE_FILENAME)
.toUriString(); .toUriString();
} }
@ -121,8 +121,8 @@ public class UserServiceImpl implements UserService {
return RandomStringUtils.randomAscii(10); return RandomStringUtils.randomAscii(10);
} }
private String generateUserId() { private UUID generateUserId() {
return UUID.randomUUID().toString(); return UUID.randomUUID();
} }
@Override @Override
@ -182,7 +182,7 @@ public class UserServiceImpl implements UserService {
throw new NotAnImageFileException(profileImage.getOriginalFilename() + " is not an image file. Please upload an image"); throw new NotAnImageFileException(profileImage.getOriginalFilename() + " is not an image file. Please upload an image");
} }
Path userFolder = Paths.get(USER_FOLDER, user.getUserId()); Path userFolder = Paths.get(USER_FOLDER, user.getUserId().toString());
try { try {
if (Files.notExists(userFolder)) { if (Files.notExists(userFolder)) {
Files.createDirectories(userFolder); Files.createDirectories(userFolder);
@ -200,7 +200,7 @@ public class UserServiceImpl implements UserService {
private void deleteProfileImageFolder(User user) { private void deleteProfileImageFolder(User user) {
Path userFolder = Paths.get(USER_FOLDER, user.getUserId()); Path userFolder = Paths.get(USER_FOLDER, user.getUserId().toString());
try { try {
FileSystemUtils.deleteRecursively(userFolder); FileSystemUtils.deleteRecursively(userFolder);
} catch (IOException exception) { } catch (IOException exception) {
@ -232,7 +232,7 @@ public class UserServiceImpl implements UserService {
} }
@Override @Override
public void deleteUser(String userId) { public void deleteUser(UUID userId) {
User userToBeDeleted = userRepository User userToBeDeleted = userRepository
.findByUserId(userId) .findByUserId(userId)
.orElseThrow(() -> new UserNotFoundException("User was not found")); .orElseThrow(() -> new UserNotFoundException("User was not found"));
@ -270,14 +270,14 @@ public class UserServiceImpl implements UserService {
} }
@Override @Override
public byte[] getImageByUserId(String userId, String filename) throws IOException { public byte[] getImageByUserId(UUID userId, String filename) throws IOException {
Path userProfileImagePath = Paths Path userProfileImagePath = Paths
.get(USER_FOLDER, userId, filename); .get(USER_FOLDER, userId.toString(), filename);
return Files.readAllBytes(userProfileImagePath); return Files.readAllBytes(userProfileImagePath);
} }
@Override @Override
public byte[] getDefaultProfileImage(String userId) { public byte[] getDefaultProfileImage(UUID userId) {
// "https://robohash.org/11951691-d373-4126-bef2-84d157a6546b" // "https://robohash.org/11951691-d373-4126-bef2-84d157a6546b"
RequestEntity<Void> requestEntity = RequestEntity RequestEntity<Void> requestEntity = RequestEntity
.get("/{userId}", userId) .get("/{userId}", userId)

View File

@ -27,7 +27,7 @@ public abstract class BaseUserTest {
protected static User user; protected static User user;
protected User createRandomUser() { protected User createRandomUser() {
String userId = UUID.randomUUID().toString(); UUID userId = UUID.randomUUID();
return User.builder() return User.builder()
.email(FAKER.bothify("????##@example.com")) .email(FAKER.bothify("????##@example.com"))
.firstName(FAKER.name().firstName()) .firstName(FAKER.name().firstName())
@ -58,11 +58,11 @@ public abstract class BaseUserTest {
.build(); .build();
} }
private String generateProfileImageUrl(String userId) { private String generateProfileImageUrl(UUID userId) {
return UriComponentsBuilder return UriComponentsBuilder
.fromUriString("http://localhost:8080") .fromUriString("http://localhost:8080")
.path(DEFAULT_USER_IMAGE_PATH) .path(DEFAULT_USER_IMAGE_PATH)
.pathSegment(userId) .pathSegment(userId.toString())
// .pathSegment(USER_IMAGE_FILENAME) // .pathSegment(USER_IMAGE_FILENAME)
.toUriString(); .toUriString();
} }

View File

@ -576,7 +576,7 @@ class UserResourceTest extends BaseUserTest {
.satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format("/user/image/profile/%s/avatar.jpg", u.getUserId()))); .satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format("/user/image/profile/%s/avatar.jpg", u.getUserId())));
User createdUser = responseEntity.getBody(); User createdUser = responseEntity.getBody();
Path path = Path.of(FileConstant.USER_FOLDER, createdUser.getUserId(), FileConstant.USER_IMAGE_FILENAME); Path path = Path.of(FileConstant.USER_FOLDER, createdUser.getUserId().toString(), FileConstant.USER_IMAGE_FILENAME);
log.debug("Path of created file: {}", path); log.debug("Path of created file: {}", path);
assertThat(Files.exists(path)).isTrue(); assertThat(Files.exists(path)).isTrue();
assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(1, ChronoUnit.SECONDS)); assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(1, ChronoUnit.SECONDS));
@ -634,7 +634,7 @@ class UserResourceTest extends BaseUserTest {
.satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format("/user/image/profile/%s/avatar.jpg", u.getUserId()))); .satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format("/user/image/profile/%s/avatar.jpg", u.getUserId())));
User createdUser = responseEntity.getBody(); User createdUser = responseEntity.getBody();
Path path = Path.of(FileConstant.USER_FOLDER, createdUser.getUserId(), FileConstant.USER_IMAGE_FILENAME); Path path = Path.of(FileConstant.USER_FOLDER, createdUser.getUserId().toString(), FileConstant.USER_IMAGE_FILENAME);
log.debug("Path of created file: {}", path); log.debug("Path of created file: {}", path);
assertThat(Files.exists(path)).isTrue(); assertThat(Files.exists(path)).isTrue();
assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(1, ChronoUnit.SECONDS)); assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(1, ChronoUnit.SECONDS));
@ -657,7 +657,7 @@ class UserResourceTest extends BaseUserTest {
superAdmin.setAuthorities(Role.ROLE_SUPER_ADMIN.getAuthorities()); superAdmin.setAuthorities(Role.ROLE_SUPER_ADMIN.getAuthorities());
String token = jwtTokenProvider.generateJwtToken(new UserPrincipal(superAdmin)); String token = jwtTokenProvider.generateJwtToken(new UserPrincipal(superAdmin));
String userId = user.getUserId(); UUID userId = user.getUserId();
//when //when
var requestEntity = RequestEntity.delete("/user/{userId}", userId) var requestEntity = RequestEntity.delete("/user/{userId}", userId)
@ -682,7 +682,7 @@ class UserResourceTest extends BaseUserTest {
User roleUser = createRandomUser(); User roleUser = createRandomUser();
String token = jwtTokenProvider.generateJwtToken(new UserPrincipal(roleUser)); String token = jwtTokenProvider.generateJwtToken(new UserPrincipal(roleUser));
String userId = user.getUserId(); UUID userId = user.getUserId();
//when //when
var requestEntity = RequestEntity.delete("/user/{userId}", userId) var requestEntity = RequestEntity.delete("/user/{userId}", userId)

View File

@ -8,6 +8,7 @@ import net.shyshkin.study.fullstack.supportportal.backend.domain.HttpResponse;
import net.shyshkin.study.fullstack.supportportal.backend.domain.Role; import net.shyshkin.study.fullstack.supportportal.backend.domain.Role;
import net.shyshkin.study.fullstack.supportportal.backend.domain.User; import net.shyshkin.study.fullstack.supportportal.backend.domain.User;
import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.UserDto; import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.UserDto;
import net.shyshkin.study.fullstack.supportportal.backend.repository.UserRepository;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -48,6 +49,9 @@ class UserResourceUnSecureTest extends BaseUserTest {
@Autowired @Autowired
TestRestTemplate restTemplate; TestRestTemplate restTemplate;
@Autowired
UserRepository userRepository;
@Nested @Nested
class AddNewUserTests { class AddNewUserTests {
@ -490,6 +494,9 @@ class UserResourceUnSecureTest extends BaseUserTest {
@Test @Test
void getAllUsers() { void getAllUsers() {
//given
long usersCount = userRepository.count();
//when //when
var responseEntity = restTemplate.exchange("/user", HttpMethod.GET, null, UserPage.class); var responseEntity = restTemplate.exchange("/user", HttpMethod.GET, null, UserPage.class);
@ -499,7 +506,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
assertThat(responseEntity.getBody()) assertThat(responseEntity.getBody())
.isNotNull(); .isNotNull();
assertThat(responseEntity.getBody().getContent()) assertThat(responseEntity.getBody().getContent())
.hasSizeGreaterThan(2); .hasSize(Math.toIntExact(usersCount));
} }
} }
@ -594,7 +601,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
.hasFieldOrPropertyWithValue("role", user.getRole()) .hasFieldOrPropertyWithValue("role", user.getRole())
.satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format("/user/image/profile/%s/avatar.jpg", user.getUserId()))); .satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format("/user/image/profile/%s/avatar.jpg", user.getUserId())));
Path path = Path.of(FileConstant.USER_FOLDER, user.getUserId(), FileConstant.USER_IMAGE_FILENAME); Path path = Path.of(FileConstant.USER_FOLDER, user.getUserId().toString(), FileConstant.USER_IMAGE_FILENAME);
log.debug("Path of created file: {}", path); log.debug("Path of created file: {}", path);
assertThat(Files.exists(path)).isTrue(); assertThat(Files.exists(path)).isTrue();
assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(100, ChronoUnit.MILLIS)); assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(100, ChronoUnit.MILLIS));
@ -739,7 +746,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
void getDefaultProfileImage_correct() throws IOException { void getDefaultProfileImage_correct() throws IOException {
//given //given
String userId = user.getUserId(); UUID userId = user.getUserId();
//when //when
RequestEntity<Void> requestEntity = RequestEntity.get("/user/image/profile/{userId}", userId) RequestEntity<Void> requestEntity = RequestEntity.get("/user/image/profile/{userId}", userId)
@ -785,7 +792,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
.hasFieldOrPropertyWithValue("role", user.getRole()) .hasFieldOrPropertyWithValue("role", user.getRole())
.satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format("/user/image/profile/%s/avatar.jpg", user.getUserId()))); .satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format("/user/image/profile/%s/avatar.jpg", user.getUserId())));
Path path = Path.of(FileConstant.USER_FOLDER, user.getUserId(), FileConstant.USER_IMAGE_FILENAME); Path path = Path.of(FileConstant.USER_FOLDER, user.getUserId().toString(), FileConstant.USER_IMAGE_FILENAME);
log.debug("Path of created file: {}", path); log.debug("Path of created file: {}", path);
assertThat(Files.exists(path)).isTrue(); assertThat(Files.exists(path)).isTrue();
assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(200, ChronoUnit.MILLIS)); assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(200, ChronoUnit.MILLIS));

View File

@ -125,7 +125,7 @@ class UserServiceTest extends BaseUserTest {
userService.updateProfileImage(username, multipartFile); userService.updateProfileImage(username, multipartFile);
//then //then
Path path = Path.of(FileConstant.USER_FOLDER, user.getUserId(), FileConstant.USER_IMAGE_FILENAME); Path path = Path.of(FileConstant.USER_FOLDER, user.getUserId().toString(), FileConstant.USER_IMAGE_FILENAME);
log.debug("Path of created file: {}", path); log.debug("Path of created file: {}", path);
assertThat(Files.exists(path)).isTrue(); assertThat(Files.exists(path)).isTrue();
assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(100, ChronoUnit.MILLIS)); assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(100, ChronoUnit.MILLIS));