27.4.1. Modified endpoints related to profile image - backend (#27)

This commit is contained in:
Art
2021-09-28 16:26:43 +03:00
parent 75517d4821
commit a1e5b6883e
9 changed files with 62 additions and 69 deletions

View File

@ -6,7 +6,7 @@ public class FileConstant {
public static final String JPG_EXTENSION = "jpg";
public static final String USER_FOLDER = System.getProperty("user.home") + "/supportportal/user/";
public static final String DIRECTORY_CREATED = "Created directory for: ";
public static final String DEFAULT_USER_IMAGE_PATH = "/user/image/profile/";
public static final String DEFAULT_USER_IMAGE_URI_PATTERN = "/user/%s/profile-image";
public static final String USER_IMAGE_FILENAME = "avatar.jpg";
public static final String FILE_SAVED_IN_FILE_SYSTEM = "Saved file in file system by name: ";
public static final String DOT = ".";

View File

@ -104,22 +104,17 @@ public class UserResource {
.build();
}
@PutMapping("{username}/profileImage")
public User updateProfileImage(@PathVariable String username, MultipartFile profileImage) {
return userService.updateProfileImage(username, profileImage);
@PutMapping("{userId}/profile-image")
public User updateProfileImage(@PathVariable UUID userId, MultipartFile profileImage) {
return userService.updateProfileImage(userId, profileImage);
}
@GetMapping(path = "{username}/image/profile", produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] getProfileImage(@PathVariable String username) throws IOException {
return userService.getProfileImage(username);
}
@GetMapping(path = "image/profile/{userId}/{filename}", produces = MediaType.IMAGE_JPEG_VALUE)
@GetMapping(path = "{userId}/profile-image/{filename}", produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] getProfileImageByUserId(@PathVariable UUID userId, @PathVariable String filename) throws IOException {
return userService.getImageByUserId(userId, filename);
}
@GetMapping(path = "image/profile/{userId}", produces = MediaType.IMAGE_JPEG_VALUE)
@GetMapping(path = "{userId}/profile-image", produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] getDefaultProfileImage(@PathVariable UUID userId) {
return userService.getDefaultProfileImage(userId);
}

View File

@ -30,9 +30,7 @@ public interface UserService extends UserDetailsService {
void resetPassword(String email);
User updateProfileImage(String username, MultipartFile profileImage);
byte[] getProfileImage(String username) throws IOException;
User updateProfileImage(UUID userId, MultipartFile profileImage);
byte[] getImageByUserId(UUID userId, String filename) throws IOException;

View File

@ -105,15 +105,13 @@ public class UserServiceImpl implements UserService {
private String generateDefaultProfileImageUrl(UUID userId) {
return ServletUriComponentsBuilder.fromCurrentContextPath()
.path(DEFAULT_USER_IMAGE_PATH)
.pathSegment(userId.toString())
.path(String.format(DEFAULT_USER_IMAGE_URI_PATTERN, userId))
.toUriString();
}
private String generateProfileImageUrl(UUID userId) {
return ServletUriComponentsBuilder.fromCurrentContextPath()
.path(DEFAULT_USER_IMAGE_PATH)
.pathSegment(userId.toString())
.path(String.format(DEFAULT_USER_IMAGE_URI_PATTERN, userId))
.pathSegment(USER_IMAGE_FILENAME)
.toUriString();
}
@ -265,20 +263,19 @@ public class UserServiceImpl implements UserService {
}
@Override
public User updateProfileImage(String username, MultipartFile profileImage) {
User user = findByUsername(username);
public User updateProfileImage(UUID userId, MultipartFile profileImage) {
User user = findByUserId(userId);
saveProfileImage(user, profileImage);
return user;
}
@Override
public byte[] getProfileImage(String username) throws IOException {
User user = findByUsername(username);
return getImageByUserId(user.getUserId(), USER_IMAGE_FILENAME);
}
@Override
public byte[] getImageByUserId(UUID userId, String filename) throws IOException {
if (!userRepository.existsByUserId(userId)) {
throw new UserNotFoundException(USER_NOT_FOUND_MSG);
}
Path userProfileImagePath = Paths
.get(USER_FOLDER, userId.toString(), filename);
return Files.readAllBytes(userProfileImagePath);

View File

@ -40,7 +40,7 @@ spring:
resources:
add-mappings: false
app:
public-urls: /user/login,/user/register,/user/*/image/**,/user/image/**
public-urls: /user/login,/user/register,/user/*/profile-image/**
cors:
allowed-origins: http://localhost:4200,https://localhost:4200,http://art-support-portal.s3-website.eu-north-1.amazonaws.com,http://portal.shyshkin.net
jwt:

View File

@ -12,7 +12,7 @@ import org.springframework.web.util.UriComponentsBuilder;
import java.time.LocalDateTime;
import java.util.UUID;
import static net.shyshkin.study.fullstack.supportportal.backend.constant.FileConstant.DEFAULT_USER_IMAGE_PATH;
import static net.shyshkin.study.fullstack.supportportal.backend.constant.FileConstant.DEFAULT_USER_IMAGE_URI_PATTERN;
import static net.shyshkin.study.fullstack.supportportal.backend.domain.Role.ROLE_ADMIN;
@SpringBootTest
@ -61,8 +61,7 @@ public abstract class BaseUserTest {
private String generateProfileImageUrl(UUID userId) {
return UriComponentsBuilder
.fromUriString("http://localhost:8080")
.path(DEFAULT_USER_IMAGE_PATH)
.pathSegment(userId.toString())
.path(String.format(DEFAULT_USER_IMAGE_URI_PATTERN, userId))
// .pathSegment(USER_IMAGE_FILENAME)
.toUriString();
}

View File

@ -7,7 +7,6 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.shyshkin.study.fullstack.supportportal.backend.common.BaseUserTest;
import net.shyshkin.study.fullstack.supportportal.backend.constant.FileConstant;
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.User;
@ -36,6 +35,7 @@ import java.time.temporal.ChronoUnit;
import java.util.Map;
import java.util.UUID;
import static net.shyshkin.study.fullstack.supportportal.backend.constant.FileConstant.*;
import static net.shyshkin.study.fullstack.supportportal.backend.constant.SecurityConstants.JWT_TOKEN_HEADER;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.within;
@ -216,7 +216,7 @@ class UserResourceTest extends BaseUserTest {
.hasFieldOrPropertyWithValue("isActive", true)
.hasFieldOrPropertyWithValue("isNotLocked", true)
.hasFieldOrPropertyWithValue("role", "ROLE_ADMIN")
.satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format("/user/image/profile/%s", u.getUserId())));
.satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format(DEFAULT_USER_IMAGE_URI_PATTERN, u.getUserId())));
String token = responseEntity.getHeaders().getFirst(JWT_TOKEN_HEADER);
log.debug("Token: {}", token);
@ -573,10 +573,10 @@ class UserResourceTest extends BaseUserTest {
.hasFieldOrPropertyWithValue("isActive", true)
.hasFieldOrPropertyWithValue("isNotLocked", true)
.hasFieldOrPropertyWithValue("role", "ROLE_ADMIN")
.satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format("/user/image/profile/%s/avatar.jpg", u.getUserId())));
.satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format(DEFAULT_USER_IMAGE_URI_PATTERN.concat("/avatar.jpg"), u.getUserId())));
User createdUser = responseEntity.getBody();
Path path = Path.of(FileConstant.USER_FOLDER, createdUser.getUserId().toString(), FileConstant.USER_IMAGE_FILENAME);
Path path = Path.of(USER_FOLDER, createdUser.getUserId().toString(), USER_IMAGE_FILENAME);
log.debug("Path of created file: {}", path);
assertThat(Files.exists(path)).isTrue();
assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(1, ChronoUnit.SECONDS));
@ -632,10 +632,10 @@ class UserResourceTest extends BaseUserTest {
.hasFieldOrPropertyWithValue("isActive", true)
.hasFieldOrPropertyWithValue("isNotLocked", true)
.hasFieldOrPropertyWithValue("role", "ROLE_ADMIN")
.satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format("/user/image/profile/%s/avatar.jpg", u.getUserId())));
.satisfies(u -> assertThat(u.getProfileImageUrl()).endsWith(String.format(DEFAULT_USER_IMAGE_URI_PATTERN.concat("/avatar.jpg"), user.getUserId())));
User createdUser = responseEntity.getBody();
Path path = Path.of(FileConstant.USER_FOLDER, createdUser.getUserId().toString(), FileConstant.USER_IMAGE_FILENAME);
Path path = Path.of(USER_FOLDER, createdUser.getUserId().toString(), USER_IMAGE_FILENAME);
log.debug("Path of created file: {}", path);
assertThat(Files.exists(path)).isTrue();
assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(1, ChronoUnit.SECONDS));

View File

@ -3,7 +3,6 @@ package net.shyshkin.study.fullstack.supportportal.backend.controller;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.shyshkin.study.fullstack.supportportal.backend.common.BaseUserTest;
import net.shyshkin.study.fullstack.supportportal.backend.constant.FileConstant;
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.User;
@ -34,6 +33,7 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import static net.shyshkin.study.fullstack.supportportal.backend.constant.FileConstant.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.within;
import static org.springframework.http.HttpStatus.*;
@ -52,6 +52,10 @@ class UserResourceUnSecureTest extends BaseUserTest {
@Autowired
UserRepository userRepository;
public static final String USER_IMAGE_ENDPOINT_TEMPLATE = "/user/{userId}/profile-image";
public static final String USER_DEFAULT_IMAGE_URI_TEMPLATE = USER_IMAGE_ENDPOINT_TEMPLATE;
public static final String USER_CUSTOM_IMAGE_URI_TEMPLATE = "/user/{userId}/profile-image/{filename}";
@Nested
class AddNewUserTests {
@ -575,7 +579,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
void updateProfileImage_correct() throws IOException {
//given
String username = user.getUsername();
UUID userId = user.getUserId();
MultipartFile profileImage = new MockMultipartFile("profileImage", "test.png",
"image/png", ("Spring Framework" + UUID.randomUUID()).getBytes());
@ -585,7 +589,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
body.add("profileImage", profileImage.getResource());
//when
var requestEntity = RequestEntity.put("/user/{username}/profileImage", username)
var requestEntity = RequestEntity.put(USER_IMAGE_ENDPOINT_TEMPLATE, userId)
.contentType(MULTIPART_FORM_DATA)
.body(body);
var responseEntity = restTemplate
@ -597,16 +601,16 @@ class UserResourceUnSecureTest extends BaseUserTest {
assertThat(responseEntity.getBody())
.isNotNull()
.hasNoNullFieldsOrPropertiesExcept("lastLoginDate", "lastLoginDateDisplay", "password", "id")
.hasFieldOrPropertyWithValue("username", username)
.hasFieldOrPropertyWithValue("username", user.getUsername())
.hasFieldOrPropertyWithValue("email", user.getEmail())
.hasFieldOrPropertyWithValue("firstName", user.getFirstName())
.hasFieldOrPropertyWithValue("lastName", user.getLastName())
.hasFieldOrPropertyWithValue("isActive", user.isActive())
.hasFieldOrPropertyWithValue("isNotLocked", user.isNotLocked())
.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(DEFAULT_USER_IMAGE_URI_PATTERN.concat("/avatar.jpg"), user.getUserId())));
Path path = Path.of(FileConstant.USER_FOLDER, user.getUserId().toString(), FileConstant.USER_IMAGE_FILENAME);
Path path = Path.of(USER_FOLDER, user.getUserId().toString(), USER_IMAGE_FILENAME);
log.debug("Path of created file: {}", path);
assertThat(Files.exists(path)).isTrue();
assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(100, ChronoUnit.MILLIS));
@ -616,8 +620,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
void updateProfileImage_absentUser() {
//given
String username = FAKER.name().username();
UUID userId = UUID.randomUUID();
MultipartFile profileImage = new MockMultipartFile("profileImage", "test.txt",
"text/plain", ("Spring Framework" + UUID.randomUUID()).getBytes());
@ -627,7 +630,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
body.add("profileImage", profileImage.getResource());
//when
var requestEntity = RequestEntity.put("/user/{username}/profileImage", username)
var requestEntity = RequestEntity.put(USER_IMAGE_ENDPOINT_TEMPLATE, userId)
.contentType(MULTIPART_FORM_DATA)
.body(body);
var responseEntity = restTemplate
@ -640,7 +643,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
.isNotNull()
.hasNoNullFieldsOrProperties()
.hasFieldOrPropertyWithValue("httpStatus", BAD_REQUEST)
.hasFieldOrPropertyWithValue("message", String.format("User with username `%s` not found", username));
.hasFieldOrPropertyWithValue("message", "User not found");
}
}
@ -660,11 +663,11 @@ class UserResourceUnSecureTest extends BaseUserTest {
void getProfileImage_correct() throws IOException {
//given
String username = user.getUsername();
uploadProfileImage(username);
UUID userId = user.getUserId();
uploadProfileImage(userId);
//when
RequestEntity<Void> requestEntity = RequestEntity.get("/user/{username}/image/profile", username)
RequestEntity<Void> requestEntity = RequestEntity.get(USER_CUSTOM_IMAGE_URI_TEMPLATE, userId, USER_IMAGE_FILENAME)
.accept(IMAGE_JPEG)
.build();
var responseEntity = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<byte[]>() {
@ -680,12 +683,12 @@ class UserResourceUnSecureTest extends BaseUserTest {
void getProfileImage_absentUser() throws IOException {
//given
String username = user.getUsername();
uploadProfileImage(username);
String absentUsername = FAKER.name().username();
UUID userId = user.getUserId();
uploadProfileImage(userId);
UUID absentUserId = UUID.randomUUID();
//when
RequestEntity<Void> requestEntity = RequestEntity.get("/user/{username}/image/profile", absentUsername)
RequestEntity<Void> requestEntity = RequestEntity.get(USER_DEFAULT_IMAGE_URI_TEMPLATE, absentUserId)
.accept(IMAGE_JPEG, APPLICATION_JSON)
.build();
var responseEntity = restTemplate.exchange(requestEntity, HttpResponse.class);
@ -697,7 +700,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
.isNotNull()
.hasNoNullFieldsOrProperties()
.hasFieldOrPropertyWithValue("httpStatus", BAD_REQUEST)
.hasFieldOrPropertyWithValue("message", String.format("User with username `%s` not found", absentUsername));
.hasFieldOrPropertyWithValue("message", "User not found");
}
@Test
@ -705,10 +708,10 @@ class UserResourceUnSecureTest extends BaseUserTest {
//given
user = userRepository.save(createRandomUser());
String username = user.getUsername();
UUID userId = user.getUserId();
//when
RequestEntity<Void> requestEntity = RequestEntity.get("/user/{username}/image/profile", username)
RequestEntity<Void> requestEntity = RequestEntity.get(USER_CUSTOM_IMAGE_URI_TEMPLATE, userId, USER_IMAGE_FILENAME)
.accept(IMAGE_JPEG, APPLICATION_JSON)
.build();
var responseEntity = restTemplate.exchange(requestEntity, HttpResponse.class);
@ -727,10 +730,10 @@ class UserResourceUnSecureTest extends BaseUserTest {
void getImageById_correct() throws IOException {
//given
String username = user.getUsername();
uploadProfileImage(username);
UUID userId = user.getUserId();
uploadProfileImage(userId);
String profileImageUrlFull = user.getProfileImageUrl();
String profileImageUrl = profileImageUrlFull.substring(profileImageUrlFull.indexOf("/user/image/profile"));
String profileImageUrl = profileImageUrlFull.substring(profileImageUrlFull.indexOf("/user/"));
log.debug("Image URL: {}", profileImageUrl);
//when
@ -754,7 +757,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
UUID userId = user.getUserId();
//when
RequestEntity<Void> requestEntity = RequestEntity.get("/user/image/profile/{userId}", userId)
RequestEntity<Void> requestEntity = RequestEntity.get(USER_DEFAULT_IMAGE_URI_TEMPLATE, userId)
.accept(IMAGE_JPEG)
.build();
var responseEntity = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<byte[]>() {
@ -773,7 +776,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
UUID userId = UUID.randomUUID();
//when
RequestEntity<Void> requestEntity = RequestEntity.get("/user/image/profile/{userId}", userId)
RequestEntity<Void> requestEntity = RequestEntity.get(USER_DEFAULT_IMAGE_URI_TEMPLATE, userId)
.accept(IMAGE_JPEG, APPLICATION_JSON)
.build();
var responseEntity = restTemplate.exchange(requestEntity, HttpResponse.class);
@ -795,7 +798,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
String userId = "not_a_UUID";
//when
RequestEntity<Void> requestEntity = RequestEntity.get("/user/image/profile/{userId}", userId)
RequestEntity<Void> requestEntity = RequestEntity.get(USER_DEFAULT_IMAGE_URI_TEMPLATE, userId)
.accept(IMAGE_JPEG, APPLICATION_JSON)
.build();
var responseEntity = restTemplate.exchange(requestEntity, HttpResponse.class);
@ -810,7 +813,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
.hasFieldOrPropertyWithValue("message", "Invalid UUID string: " + userId);
}
private void uploadProfileImage(String username) throws IOException {
private void uploadProfileImage(UUID userId) throws IOException {
MultipartFile profileImage = new MockMultipartFile("profileImage", "test.jpg",
IMAGE_JPEG_VALUE, ("Spring Framework" + UUID.randomUUID()).getBytes());
@ -820,7 +823,7 @@ class UserResourceUnSecureTest extends BaseUserTest {
body.add("profileImage", profileImage.getResource());
//when
var requestEntity = RequestEntity.put("/user/{username}/profileImage", username)
var requestEntity = RequestEntity.put(USER_IMAGE_ENDPOINT_TEMPLATE, userId)
.contentType(MULTIPART_FORM_DATA)
.body(body);
var responseEntity = restTemplate
@ -832,16 +835,17 @@ class UserResourceUnSecureTest extends BaseUserTest {
assertThat(responseEntity.getBody())
.isNotNull()
.hasNoNullFieldsOrPropertiesExcept("lastLoginDate", "lastLoginDateDisplay", "password", "id")
.hasFieldOrPropertyWithValue("username", username)
.hasFieldOrPropertyWithValue("userId", userId)
.hasFieldOrPropertyWithValue("username", user.getUsername())
.hasFieldOrPropertyWithValue("email", user.getEmail())
.hasFieldOrPropertyWithValue("firstName", user.getFirstName())
.hasFieldOrPropertyWithValue("lastName", user.getLastName())
.hasFieldOrPropertyWithValue("isActive", user.isActive())
.hasFieldOrPropertyWithValue("isNotLocked", user.isNotLocked())
.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(DEFAULT_USER_IMAGE_URI_PATTERN.concat("/avatar.jpg"), user.getUserId())));
Path path = Path.of(FileConstant.USER_FOLDER, user.getUserId().toString(), FileConstant.USER_IMAGE_FILENAME);
Path path = Path.of(USER_FOLDER, user.getUserId().toString(), USER_IMAGE_FILENAME);
log.debug("Path of created file: {}", path);
assertThat(Files.exists(path)).isTrue();
assertThat(Files.getLastModifiedTime(path).toInstant()).isCloseTo(Instant.now(), within(200, ChronoUnit.MILLIS));

View File

@ -117,12 +117,12 @@ class UserServiceTest extends BaseUserTest {
//given
User fakeUser = createRandomUser();
user = userRepository.save(fakeUser);
String username = user.getUsername();
UUID userId = user.getUserId();
//when
MockMultipartFile multipartFile = new MockMultipartFile("file", "test.jpg",
"image/jpeg", ("Spring Framework" + UUID.randomUUID()).getBytes());
userService.updateProfileImage(username, multipartFile);
userService.updateProfileImage(userId, multipartFile);
//then
Path path = Path.of(FileConstant.USER_FOLDER, user.getUserId().toString(), FileConstant.USER_IMAGE_FILENAME);