diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/user/UserController.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/user/UserController.java index 8a449dca0341cb1548bce2ccca4379615cf5bae4..d7571c7a9189e5828967dcdc0c849a7d7c1a7626 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/user/UserController.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/user/UserController.java @@ -5,14 +5,17 @@ import lombok.AllArgsConstructor; import ntnu.idatt2016.v233.SmartMat.dto.request.AllergyRequest; import ntnu.idatt2016.v233.SmartMat.dto.request.RegisterUserRequest; import ntnu.idatt2016.v233.SmartMat.dto.enums.Authority; +import ntnu.idatt2016.v233.SmartMat.dto.request.UpdateUserRequest; import ntnu.idatt2016.v233.SmartMat.entity.user.User; import ntnu.idatt2016.v233.SmartMat.service.user.UserService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; +import java.util.Optional; /** @@ -102,4 +105,70 @@ public class UserController { .map(user -> ResponseEntity.ok(user.getAllergies().size() > 0)) .orElseGet(() -> ResponseEntity.notFound().build()); } + + /** + * Update a user in the database. + * @param username The username of the user to be updated. + * @param updateUser The new values for the user. + * @param authentication The authentication object of the user. + * @return The updated user. + */ + @PutMapping("/update/{username}") + public ResponseEntity<User> updateUser(@PathVariable String username, @RequestBody UpdateUserRequest updateUser, + Authentication authentication) { + if(!username.equals(authentication.getName())) + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + + Optional<User> user = userService.getUserFromUsername(username); + + + if(user.isEmpty()) + return ResponseEntity.notFound().build(); + + User userEntity = user.get(); + + if(updateUser.firstName() != null && + !updateUser.firstName().trim().isEmpty() && updateUser.firstName().length() <= 50){ + userEntity.setFirstName(updateUser.firstName()); + } + + if(updateUser.lastName() != null && + !updateUser.lastName().trim().isEmpty() && updateUser.lastName().length() <= 50){ + userEntity.setLastName(updateUser.lastName()); + } + + if(updateUser.email() != null && + !updateUser.email().trim().isEmpty() && updateUser.email().length() <= 50){ + userEntity.setEmail(updateUser.email()); + } + + if(updateUser.password() != null && + !updateUser.password().trim().isEmpty() && updateUser.password().length() <= 50){ + userEntity.setPassword(passwordEncoder.encode(updateUser.password())); + } + + if(updateUser.birthDate() != null){ + userEntity.setDateOfBirth(updateUser.birthDate()); + } + + + if(updateUser.allergies() != null){ + userEntity.getAllergies().stream().filter(allergy -> !updateUser.allergies().contains(allergy.getName())) + .forEach(allergy -> userService.removeAllergyFromUser(username, allergy.getName())); + + updateUser.allergies().stream().filter(allergy -> userEntity.getAllergies().stream() + .noneMatch(allergy1 -> allergy1.getName().equals(allergy))).forEach( + allergy -> userService.addAllergyToUser(username, allergy) + ); + + } + + userService.updateUser(userEntity); + + + userEntity.setPassword(null); + + return ResponseEntity.ok(userEntity); + + } } \ No newline at end of file diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/dto/request/UpdateUserRequest.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/dto/request/UpdateUserRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..159107c397361cc9931851ef3935e3f2cc4acd13 --- /dev/null +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/dto/request/UpdateUserRequest.java @@ -0,0 +1,22 @@ +package ntnu.idatt2016.v233.SmartMat.dto.request; + +import java.sql.Date; +import java.util.List; + +/** + * This class represents a request to update a user + * @author Birk + * @version 1.0 + * @since 26.04.2023 + * + * @param firstName the first name of the user + * @param lastName the last name of the user + * @param email the email of the user + * @param password the password of the user + * @param birthDate the birth date of the user + * @param allergies the allergies of the user + */ +public record UpdateUserRequest(String firstName, String lastName, + String email, String password, Date birthDate, + List<String> allergies) { +} diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/user/User.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/user/User.java index 71588350df5c87ad3c57a47072e6da29695b6ef9..e9cf55479aa16f32ca269a7a528266381731c2cb 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/user/User.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/user/User.java @@ -4,10 +4,7 @@ package ntnu.idatt2016.v233.SmartMat.entity.user; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.*; import ntnu.idatt2016.v233.SmartMat.dto.enums.Authority; import ntnu.idatt2016.v233.SmartMat.entity.Recipe; import ntnu.idatt2016.v233.SmartMat.entity.group.UserGroupAsso; @@ -31,6 +28,7 @@ import java.util.*; @Data @NoArgsConstructor +@EqualsAndHashCode @AllArgsConstructor @Entity(name = "users") @Builder diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/user/UserService.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/user/UserService.java index b3990b550255d423bfeeeb5a609244291a6ee399..4fce289e2eab5ffd8e83878cbb2b69883178edcb 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/user/UserService.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/user/UserService.java @@ -173,4 +173,25 @@ public class UserService { } return Optional.empty(); } + + /** + * Removes allergy from user + * @param username username of user + * @param allergyName name of allergy + * @return user with removed allergy + */ + public Optional<User> removeAllergyFromUser(String username, String allergyName){ + Optional<User> user = userRepository.findByUsername(username); + Optional<Allergy> allergy = allergyRepository.findByName(allergyName); + + if (user.isPresent() && allergy.isPresent()){ + user.get().getAllergies().remove(allergy.get()); + return Optional.of(userRepository.save(user.get())); + } else if (user.isEmpty()) { + throw new EntityNotFoundException("User not found"); + } else if (allergy.isEmpty()) { + throw new EntityNotFoundException("Allergy not found"); + } + return Optional.empty(); + } } \ No newline at end of file diff --git a/src/test/java/ntnu/idatt2016/v233/SmartMat/controller/user/UserControllerTest.java b/src/test/java/ntnu/idatt2016/v233/SmartMat/controller/user/UserControllerTest.java index 648056c87096c7475cd38b467d047a6f4d5320d2..205fcabe07f52fd9ab9442d250d1d6f4585ff72f 100644 --- a/src/test/java/ntnu/idatt2016/v233/SmartMat/controller/user/UserControllerTest.java +++ b/src/test/java/ntnu/idatt2016/v233/SmartMat/controller/user/UserControllerTest.java @@ -1,20 +1,30 @@ package ntnu.idatt2016.v233.SmartMat.controller.user; import ntnu.idatt2016.v233.SmartMat.dto.request.RegisterUserRequest; +import ntnu.idatt2016.v233.SmartMat.dto.request.UpdateUserRequest; import ntnu.idatt2016.v233.SmartMat.entity.user.User; import ntnu.idatt2016.v233.SmartMat.repository.user.UserRepository; +import ntnu.idatt2016.v233.SmartMat.service.user.UserService; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.crypto.password.PasswordEncoder; import java.sql.Date; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class UserControllerTest { @@ -25,6 +35,30 @@ class UserControllerTest { @Autowired private UserRepository userRepository; + @Mock + private UserService userService; + + @Mock + private PasswordEncoder passwordEncoder; + + + @InjectMocks + private UserController userController; + + + private UpdateUserRequest updateUser; + + private Authentication authentication; + + @BeforeEach + public void setup() { + updateUser = new UpdateUserRequest( + "John", "Doe", "johndoe@example.com", "newPassword123", + Date.valueOf("1980-01-01"), List.of("Peanut", "Lactose") + ); + authentication = mock(Authentication.class); + } + @Test void registerUser_validRequest_shouldReturnCreatedUser() { // Arrange @@ -54,4 +88,39 @@ class UserControllerTest { assertTrue(userOptional.isPresent()); } + + + @Test + public void testUpdateUserWithValidUsernameAndRequestBodyReturnsOk() { + // Arrange + String username = "johndoe"; + + User user = new User(); + user.setUsername(username); + user.setFirstName("John"); + user.setLastName("Doe"); + user.setEmail("johndoe@example.com"); + user.setPassword("oldPassword123"); + user.setAllergies(new ArrayList<>()); + user.setDateOfBirth(Date.valueOf("1980-01-01")); + when(authentication.getName()).thenReturn(username); + when(userService.getUserFromUsername(username)).thenReturn(Optional.of(user)); + when(passwordEncoder.encode(updateUser.password())).thenReturn("encodedPassword"); + + when(userService.updateUser(user)).thenReturn(user); + + // Act + ResponseEntity<User> response = userController.updateUser(username, updateUser, authentication); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + assertEquals("John", response.getBody().getFirstName()); + assertEquals("Doe", response.getBody().getLastName()); + assertEquals("johndoe@example.com", response.getBody().getEmail()); + assertNull(response.getBody().getPassword()); + assertEquals(Date.valueOf("1980-01-01"), response.getBody().getDateOfBirth()); + + verify(userService, times(1)).updateUser(user); + } } \ No newline at end of file