diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResource.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResource.java index 7e2f7d9..0fe64d1 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResource.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResource.java @@ -8,6 +8,7 @@ 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.service.UserService; import net.shyshkin.study.fullstack.supportportal.backend.utility.JwtTokenProvider; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -17,6 +18,7 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.validation.Valid; +import java.io.IOException; import java.util.List; import static org.springframework.http.HttpStatus.OK; @@ -108,6 +110,13 @@ public class UserResource { return userService.updateProfileImage(username, profileImage); } + @GetMapping(path = "{username}/image/profile", produces = MediaType.IMAGE_JPEG_VALUE) + public byte[] getProfileImage(@PathVariable String username) throws IOException { + byte[] profileImage = userService.getProfileImage(username); + log.debug("File size: {}", profileImage.length); + return profileImage; + } + private void authenticate(String username, String password) { Authentication auth = new UsernamePasswordAuthenticationToken(username, password); authenticationManager.authenticate(auth); diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserService.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserService.java index 450a893..3aaf6df 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserService.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserService.java @@ -5,6 +5,7 @@ import net.shyshkin.study.fullstack.supportportal.backend.domain.dto.UserDto; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.util.List; public interface UserService extends UserDetailsService { @@ -27,4 +28,6 @@ public interface UserService extends UserDetailsService { User updateProfileImage(String username, MultipartFile profileImage); + byte[] getProfileImage(String username) throws IOException; + } diff --git a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserServiceImpl.java b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserServiceImpl.java index a8eb526..655d3ac 100644 --- a/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserServiceImpl.java +++ b/support-portal-backend/src/main/java/net/shyshkin/study/fullstack/supportportal/backend/service/UserServiceImpl.java @@ -217,6 +217,14 @@ public class UserServiceImpl implements UserService { return user; } + @Override + public byte[] getProfileImage(String username) throws IOException { + User user = findByUsername(username); + Path userFolder = Paths + .get(USER_FOLDER, user.getUserId(), USER_IMAGE_FILENAME); + return Files.readAllBytes(userFolder); + } + private void validateNewUsernameAndEmail(String username, String email) { if (userRepository.existsByUsername(username)) diff --git a/support-portal-backend/src/main/resources/application.yml b/support-portal-backend/src/main/resources/application.yml index b68ae2d..393b97b 100644 --- a/support-portal-backend/src/main/resources/application.yml +++ b/support-portal-backend/src/main/resources/application.yml @@ -35,7 +35,7 @@ spring: # resources: # add-mappings: false app: - public-urls: /user/login,/user/register,/user/image/** + public-urls: /user/login,/user/register,/user/*/image/** jwt: secret: VeRy_5ecretP@55W0rd! # secret: ${random.value} #Does not work - every time generates new value diff --git a/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResourceUnSecureTest.java b/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResourceUnSecureTest.java index 78f893a..2ae17cf 100644 --- a/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResourceUnSecureTest.java +++ b/support-portal-backend/src/test/java/net/shyshkin/study/fullstack/supportportal/backend/controller/UserResourceUnSecureTest.java @@ -35,8 +35,7 @@ import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.within; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.OK; +import static org.springframework.http.HttpStatus.*; @Slf4j @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -650,4 +649,121 @@ class UserResourceUnSecureTest extends BaseUserTest { .hasFieldOrPropertyWithValue("message", String.format("User with username `%s` not found", username).toUpperCase()); } } + + @Nested + class GetProfileImageTests { + + @BeforeEach + void setUp() { + user = userRepository + .findAll() + .stream() + .findAny() + .orElseGet(() -> userRepository.save(createRandomUser())); + } + + @Test + void getProfileImage_correct() throws IOException { + + //given + String username = user.getUsername(); + uploadProfileImage(username); + + //when + RequestEntity requestEntity = RequestEntity.get("/user/{username}/image/profile", username) + .accept(MediaType.IMAGE_JPEG) + .build(); + var responseEntity = restTemplate.exchange(requestEntity, new ParameterizedTypeReference() { + }); + + //then + log.debug("Response Entity: {}", responseEntity); + assertThat(responseEntity.getStatusCode()).isEqualTo(OK); + assertThat(responseEntity.getBody()).hasSize(52); + } + + @Test + void getProfileImage_absentUser() throws IOException { + + //given + String username = user.getUsername(); + uploadProfileImage(username); + String absentUsername = FAKER.name().username(); + + //when + RequestEntity requestEntity = RequestEntity.get("/user/{username}/image/profile", absentUsername) + .accept(MediaType.IMAGE_JPEG, MediaType.APPLICATION_JSON) + .build(); + var responseEntity = restTemplate.exchange(requestEntity, HttpResponse.class); + + //then + log.debug("Response Entity: {}", responseEntity); + assertThat(responseEntity.getStatusCode()).isEqualTo(BAD_REQUEST); + assertThat(responseEntity.getBody()) + .isNotNull() + .hasNoNullFieldsOrProperties() + .hasFieldOrPropertyWithValue("httpStatus", BAD_REQUEST) + .hasFieldOrPropertyWithValue("message", String.format("User with username `%s` not found", absentUsername).toUpperCase()); + } + + @Test + void getProfileImage_absentImage() { + + //given + user = userRepository.save(createRandomUser()); + String username = user.getUsername(); + + //when + RequestEntity requestEntity = RequestEntity.get("/user/{username}/image/profile", username) + .accept(MediaType.IMAGE_JPEG, MediaType.APPLICATION_JSON) + .build(); + var responseEntity = restTemplate.exchange(requestEntity, HttpResponse.class); + + //then + log.debug("Response Entity: {}", responseEntity); + assertThat(responseEntity.getStatusCode()).isEqualTo(INTERNAL_SERVER_ERROR); + assertThat(responseEntity.getBody()) + .isNotNull() + .hasNoNullFieldsOrProperties() + .hasFieldOrPropertyWithValue("httpStatus", INTERNAL_SERVER_ERROR) + .hasFieldOrPropertyWithValue("message", "Error occurred while processing file".toUpperCase()); + } + + private void uploadProfileImage(String username) throws IOException { + + MultipartFile profileImage = new MockMultipartFile("profileImage", "test.txt", + "text/plain", ("Spring Framework" + UUID.randomUUID()).getBytes()); + + MultiValueMap body + = new LinkedMultiValueMap<>(); + body.add("profileImage", profileImage.getResource()); + + //when + var requestEntity = RequestEntity.put("/user/{username}/profileImage", username) + .contentType(MediaType.MULTIPART_FORM_DATA) + .body(body); + var responseEntity = restTemplate + .exchange(requestEntity, User.class); + + //then + log.debug("Response Entity: {}", responseEntity); + assertThat(responseEntity.getStatusCode()).isEqualTo(OK); + assertThat(responseEntity.getBody()) + .isNotNull() + .hasNoNullFieldsOrPropertiesExcept("lastLoginDate", "lastLoginDateDisplay") + .hasFieldOrPropertyWithValue("username", username) + .hasFieldOrPropertyWithValue("email", user.getEmail()) + .hasFieldOrPropertyWithValue("firstName", user.getFirstName()) + .hasFieldOrPropertyWithValue("lastName", user.getLastName()) + .hasFieldOrPropertyWithValue("isActive", user.isActive()) + .hasFieldOrPropertyWithValue("isNotLocked", user.isNotLocked()) + .hasFieldOrPropertyWithValue("role", user.getRole()); + + Path path = Path.of(FileConstant.USER_FOLDER, user.getUserId(), FileConstant.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)); + } + } + } \ No newline at end of file