diff --git a/pom.xml b/pom.xml index 51f7bca..ec8d1a3 100644 --- a/pom.xml +++ b/pom.xml @@ -28,13 +28,35 @@ 21 - + 1.6.2 + org.springframework.boot spring-boot-starter-web + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + provided + + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + provided + + org.springframework.boot spring-boot-devtools @@ -71,6 +93,16 @@ maven-compiler-plugin + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + org.projectlombok lombok diff --git a/src/main/java/com/skillsync/skillsync/controller/UserController.java b/src/main/java/com/skillsync/skillsync/controller/UserController.java index ddab9cb..dec9e91 100644 --- a/src/main/java/com/skillsync/skillsync/controller/UserController.java +++ b/src/main/java/com/skillsync/skillsync/controller/UserController.java @@ -5,15 +5,24 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; +import com.skillsync.skillsync.dto.UserSearchResponseDTO; import com.skillsync.skillsync.dto.UserUpdateDTO; import com.skillsync.skillsync.model.Skill; import com.skillsync.skillsync.model.User; import com.skillsync.skillsync.service.SkillService; import com.skillsync.skillsync.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import com.skillsync.skillsync.dto.UserSearchResponseDTO; - -import java.util.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; import java.util.stream.Collectors; @RestController @@ -58,7 +67,7 @@ public List getUserSkills(@PathVariable Long id) { @PutMapping("/update/{id}") public User updateUser(@PathVariable Long id, @RequestBody UserUpdateDTO user) { // TODO: Implement update user logic (call service layer) - return null; + return userService.updateUser(id, user); } diff --git a/src/main/java/com/skillsync/skillsync/dto/SkillDTO.java b/src/main/java/com/skillsync/skillsync/dto/SkillDTO.java index 79f22c1..4bdfe38 100644 --- a/src/main/java/com/skillsync/skillsync/dto/SkillDTO.java +++ b/src/main/java/com/skillsync/skillsync/dto/SkillDTO.java @@ -1,27 +1,30 @@ package com.skillsync.skillsync.dto; import com.skillsync.skillsync.model.Skill; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +@Setter +@Getter public class SkillDTO { private Long id; private String name; private String description; - // Static mapper to convert Entity to DTO public static SkillDTO fromEntity(Skill skill) { SkillDTO dto = new SkillDTO(); // Assuming Skill.getId() maps to skill_id, and so on - dto.id = skill.getId(); + dto.id = skill.getId(); dto.name = skill.getName(); dto.description = skill.getDescription(); return dto; } - - // Getters and Setters - public Long getId() { return id; } - public void setId(Long id) { this.id = id; } - public String getName() { return name; } - public void setName(String name) { this.name = name; } - public String getDescription() { return description; } - public void setDescription(String description) { this.description = description; } } \ No newline at end of file diff --git a/src/main/java/com/skillsync/skillsync/dto/SkillMapper.java b/src/main/java/com/skillsync/skillsync/dto/SkillMapper.java new file mode 100644 index 0000000..a5a4318 --- /dev/null +++ b/src/main/java/com/skillsync/skillsync/dto/SkillMapper.java @@ -0,0 +1,10 @@ +package com.skillsync.skillsync.dto; + +import com.skillsync.skillsync.model.Skill; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface SkillMapper { + SkillDTO fromEntityToDto(Skill skill); + Skill fromDtoToEntity(SkillDTO skillDTO); +} diff --git a/src/main/java/com/skillsync/skillsync/dto/UserMapper.java b/src/main/java/com/skillsync/skillsync/dto/UserMapper.java new file mode 100644 index 0000000..13ec9bd --- /dev/null +++ b/src/main/java/com/skillsync/skillsync/dto/UserMapper.java @@ -0,0 +1,11 @@ +package com.skillsync.skillsync.dto; + +import com.skillsync.skillsync.model.User; +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface UserMapper { + + User fromDtoToEntity(UserUpdateDTO userUpdateDTO); + UserUpdateDTO fromEntityToDto(User user); +} diff --git a/src/main/java/com/skillsync/skillsync/dto/UserSearchResponseDTO.java b/src/main/java/com/skillsync/skillsync/dto/UserSearchResponseDTO.java index c8e8474..325ff42 100644 --- a/src/main/java/com/skillsync/skillsync/dto/UserSearchResponseDTO.java +++ b/src/main/java/com/skillsync/skillsync/dto/UserSearchResponseDTO.java @@ -1,6 +1,7 @@ package com.skillsync.skillsync.dto; import com.skillsync.skillsync.model.User; + import java.util.List; import java.util.stream.Collectors; diff --git a/src/main/java/com/skillsync/skillsync/dto/UserUpdateDTO.java b/src/main/java/com/skillsync/skillsync/dto/UserUpdateDTO.java index 4d14986..f68f861 100644 --- a/src/main/java/com/skillsync/skillsync/dto/UserUpdateDTO.java +++ b/src/main/java/com/skillsync/skillsync/dto/UserUpdateDTO.java @@ -1,8 +1,22 @@ package com.skillsync.skillsync.dto; -public class UserUpdateDTO { +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; - // Add feilds to update user - // this file helps not to senf all the fields of user entity to update +import java.util.List; +@AllArgsConstructor +@NoArgsConstructor +@Data +@Setter +@Getter +@Builder +public class UserUpdateDTO { + private String name; + private String bio; + private List skills; } diff --git a/src/main/java/com/skillsync/skillsync/model/Skill.java b/src/main/java/com/skillsync/skillsync/model/Skill.java index f34260f..66a3fe4 100644 --- a/src/main/java/com/skillsync/skillsync/model/Skill.java +++ b/src/main/java/com/skillsync/skillsync/model/Skill.java @@ -1,15 +1,33 @@ package com.skillsync.skillsync.model; import com.fasterxml.jackson.annotation.JsonBackReference; -import jakarta.persistence.*; - +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +@Setter +@Getter @Entity @Table(name = "skills") public class Skill { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long skill_id; + private Long id; private String name; @@ -21,47 +39,4 @@ public class Skill { @JsonBackReference private User user; - // Constructors - public Skill() { - } - - public Skill(String name, String description, User user) { - this.name = name; - this.description = description; - this.user = user; - } - - // Getters and Setters - public Long getId() { - return skill_id; - } - - public void setId(Long id) { - this.skill_id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public User getUser() { - return user; - } - - public void setUser(User user) { - this.user = user; - } - } diff --git a/src/main/java/com/skillsync/skillsync/model/User.java b/src/main/java/com/skillsync/skillsync/model/User.java index 4377f30..4ae6cac 100644 --- a/src/main/java/com/skillsync/skillsync/model/User.java +++ b/src/main/java/com/skillsync/skillsync/model/User.java @@ -1,16 +1,39 @@ package com.skillsync.skillsync.model; import com.fasterxml.jackson.annotation.JsonManagedReference; -import jakarta.persistence.*; -import java.util.*; - +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +@Setter +@Getter @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long user_id; + private Long id; private String name; @@ -30,83 +53,5 @@ public class User { @JsonManagedReference private List skills = new ArrayList<>(); - // Constructors - public User() { - } - - public User(String name, String email, String password, String bio, RoleType roleType, - boolean availableForMentorship) { - this.name = name; - this.email = email; - this.password = password; - this.bio = bio; - this.roleType = roleType; - this.availableForMentorship = availableForMentorship; - } - - // Getters and Setters - public Long getId() { - return user_id; - } - - public void setId(Long id) { - this.user_id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getBio() { - return bio; - } - - public void setBio(String bio) { - this.bio = bio; - } - - public RoleType getRoleType() { - return roleType; - } - - public void setRoleType(RoleType roleType) { - this.roleType = roleType; - } - - public boolean isAvailableForMentorship() { - return availableForMentorship; - } - - public void setAvailableForMentorship(boolean availableForMentorship) { - this.availableForMentorship = availableForMentorship; - } - - public List getSkills() { - return skills; - } - - public void setSkills(List skills) { - this.skills = skills; - } } diff --git a/src/main/java/com/skillsync/skillsync/repository/SkillRepository.java b/src/main/java/com/skillsync/skillsync/repository/SkillRepository.java index e9106e4..f893560 100644 --- a/src/main/java/com/skillsync/skillsync/repository/SkillRepository.java +++ b/src/main/java/com/skillsync/skillsync/repository/SkillRepository.java @@ -1,17 +1,14 @@ package com.skillsync.skillsync.repository; +import com.skillsync.skillsync.model.Skill; import com.skillsync.skillsync.model.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import com.skillsync.skillsync.model.Skill; - import java.util.List; @Repository public interface SkillRepository extends JpaRepository { // Future custom queries like findBySkillName can be added here - - List findByUser(User user); } diff --git a/src/main/java/com/skillsync/skillsync/repository/UserRepository.java b/src/main/java/com/skillsync/skillsync/repository/UserRepository.java index 359b917..6031cd4 100644 --- a/src/main/java/com/skillsync/skillsync/repository/UserRepository.java +++ b/src/main/java/com/skillsync/skillsync/repository/UserRepository.java @@ -1,16 +1,12 @@ package com.skillsync.skillsync.repository; +import com.skillsync.skillsync.model.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -import com.skillsync.skillsync.model.User; import java.util.List; -@Repository public interface UserRepository extends JpaRepository { // Custom query methods can be added later, e.g. findByEmail, findBySkillName, // etc. diff --git a/src/main/java/com/skillsync/skillsync/service/SkillService.java b/src/main/java/com/skillsync/skillsync/service/SkillService.java index 2b7b882..0866dc5 100644 --- a/src/main/java/com/skillsync/skillsync/service/SkillService.java +++ b/src/main/java/com/skillsync/skillsync/service/SkillService.java @@ -1,9 +1,9 @@ package com.skillsync.skillsync.service; -import java.util.List; - import com.skillsync.skillsync.model.Skill; +import java.util.List; + public interface SkillService { List getSkillsByUserId(Long userId); diff --git a/src/main/java/com/skillsync/skillsync/service/UserService.java b/src/main/java/com/skillsync/skillsync/service/UserService.java index 68df387..b88aae5 100644 --- a/src/main/java/com/skillsync/skillsync/service/UserService.java +++ b/src/main/java/com/skillsync/skillsync/service/UserService.java @@ -1,11 +1,12 @@ package com.skillsync.skillsync.service; -import java.util.*; - import com.skillsync.skillsync.dto.UserDTO; +import com.skillsync.skillsync.dto.UserUpdateDTO; import com.skillsync.skillsync.model.Skill; import com.skillsync.skillsync.model.User; +import java.util.List; + public interface UserService { User saveUser(UserDTO userDTO); @@ -14,7 +15,7 @@ public interface UserService { User getUserById(Long id); // Update User - User updateUser(Long id, User user); + User updateUser(Long id, UserUpdateDTO user); // Add Skill to existing User User addSkillToUser(Long userId, Skill skill); diff --git a/src/main/java/com/skillsync/skillsync/service/impl/SkillServiceImpl.java b/src/main/java/com/skillsync/skillsync/service/impl/SkillServiceImpl.java index 8bb24ae..c044dbd 100644 --- a/src/main/java/com/skillsync/skillsync/service/impl/SkillServiceImpl.java +++ b/src/main/java/com/skillsync/skillsync/service/impl/SkillServiceImpl.java @@ -1,36 +1,27 @@ package com.skillsync.skillsync.service.impl; -import java.util.*; - import com.skillsync.skillsync.model.Skill; -import com.skillsync.skillsync.model.User; import com.skillsync.skillsync.repository.SkillRepository; import com.skillsync.skillsync.repository.UserRepository; import com.skillsync.skillsync.service.SkillService; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.Collections; +import java.util.List; + @Service +@RequiredArgsConstructor public class SkillServiceImpl implements SkillService { - final private UserRepository userRepository; - final private SkillRepository skillRepository; - - @Autowired - public SkillServiceImpl(SkillRepository skillRepository,UserRepository userRepository){ - this.skillRepository=skillRepository; - this.userRepository=userRepository; - } + private final UserRepository userRepository; + private final SkillRepository skillRepository; @Override public List getSkillsByUserId(Long userId) { - Optional userOpt = userRepository.findById(userId); - if (userOpt.isEmpty()) { - return List.of(); - } - User user = userOpt.get(); - List skills = skillRepository.findByUser(user); - return skills.isEmpty() ? List.of() : skills; + return userRepository.findById(userId) + .map(skillRepository::findByUser) + .orElse(Collections.emptyList()); } } diff --git a/src/main/java/com/skillsync/skillsync/service/impl/UserServiceImpl.java b/src/main/java/com/skillsync/skillsync/service/impl/UserServiceImpl.java index 09e0123..65fb1be 100644 --- a/src/main/java/com/skillsync/skillsync/service/impl/UserServiceImpl.java +++ b/src/main/java/com/skillsync/skillsync/service/impl/UserServiceImpl.java @@ -8,25 +8,30 @@ import com.skillsync.skillsync.dto.UserDTO; import org.springframework.beans.factory.annotation.Autowired; +import com.skillsync.skillsync.dto.SkillMapper; +import com.skillsync.skillsync.dto.UserMapper; +import com.skillsync.skillsync.dto.UserUpdateDTO; import com.skillsync.skillsync.model.Skill; import com.skillsync.skillsync.model.User; import com.skillsync.skillsync.repository.UserRepository; import com.skillsync.skillsync.service.UserService; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import com.skillsync.skillsync.repository.UserRepository; +import org.springframework.util.CollectionUtils; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static java.util.Optional.ofNullable; @Service -public class UserServiceImpl implements UserService +@RequiredArgsConstructor +public class UserServiceImpl implements UserService { private final UserRepository userRepository; - - //Constructor for Dependency Injection - @Autowired - public UserServiceImpl(UserRepository userRepository) { - this.userRepository = userRepository; - } - + private final UserMapper userMapper; + private final SkillMapper skillMapper; /** * @@ -64,23 +69,42 @@ public List getAllUsers() { @Override public User getUserById(Long id) { - Optional userOptional = userRepository.findById(id); - - if(!userOptional.isPresent()) - { - throw new RuntimeException("User Not Found"); - } - return userOptional.get(); + return userRepository.findById(id) + .orElseThrow(() -> new RuntimeException("User Not Found")); } // Update User @Override - public User updateUser(Long id, User user) { + public User updateUser(Long id, UserUpdateDTO userUpdateDTO) { // TODO: Fetch existing user by ID // TODO: Update only provided fields // TODO: Save and return updated user - return null; + User existingUser = getUserById(id); + + UserUpdateDTO existingDto = userMapper.fromEntityToDto(existingUser); + + UserUpdateDTO.UserUpdateDTOBuilder builder = UserUpdateDTO.builder(); + ofNullable(existingDto.getBio()).ifPresent(builder::bio); + ofNullable(existingDto.getName()).ifPresent(builder::name); + if (!CollectionUtils.isEmpty(userUpdateDTO.getSkills())) { + builder.skills(userUpdateDTO.getSkills()); + } + UserUpdateDTO updateDTO = builder.build(); + User updatedUser = userMapper.fromDtoToEntity(updateDTO); + + updatedUser.setId(existingUser.getId()); + if (!CollectionUtils.isEmpty(updateDTO.getSkills())) { + updatedUser.getSkills().clear(); + User finalUpdatedUser = updatedUser; + List updatedSkills = updateDTO.getSkills().stream() + .map(skillMapper::fromDtoToEntity) + .peek(skill -> skill.setUser(finalUpdatedUser)) + .toList(); + updatedUser.setSkills(updatedSkills); + } + updatedUser = userRepository.save(updatedUser); + return updatedUser; } diff --git a/src/test/java/com/skillsync/skillsync/SkillsyncApplicationTests.java b/src/test/java/com/skillsync/skillsync/SkillsyncApplicationTests.java deleted file mode 100644 index e5ea664..0000000 --- a/src/test/java/com/skillsync/skillsync/SkillsyncApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.skillsync.skillsync; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class SkillsyncApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/src/test/java/com/skillsync/skillsync/service/impl/UserServiceImplTest.java b/src/test/java/com/skillsync/skillsync/service/impl/UserServiceImplTest.java new file mode 100644 index 0000000..3fb7cab --- /dev/null +++ b/src/test/java/com/skillsync/skillsync/service/impl/UserServiceImplTest.java @@ -0,0 +1,76 @@ +package com.skillsync.skillsync.service.impl; + + +import com.skillsync.skillsync.dto.SkillDTO; +import com.skillsync.skillsync.dto.SkillMapper; +import com.skillsync.skillsync.dto.SkillMapperImpl; +import com.skillsync.skillsync.dto.UserMapper; +import com.skillsync.skillsync.dto.UserMapperImpl; +import com.skillsync.skillsync.dto.UserUpdateDTO; +import com.skillsync.skillsync.model.Skill; +import com.skillsync.skillsync.model.User; +import com.skillsync.skillsync.repository.UserRepository; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class UserServiceImplTest { + + @InjectMocks + private UserServiceImpl userService; + + @Mock + private UserRepository userRepository; + + @Spy + private UserMapper userMapper = new UserMapperImpl(); + + @Spy + private SkillMapper skillMapper = new SkillMapperImpl(); + + @Test + void testUpdateUser() { + // arrange + final Long id = 1L; + final UserUpdateDTO userUpdateDTO = UserUpdateDTO.builder() + .name("Piolo") + .skills(List.of( + SkillDTO.builder() + .name("Java") + .build() + )) + .build(); + User user = User.builder() + .name("Piolo") + .skills(List.of(Skill.builder() + .name("Java") + .build())) + .build(); + when(userRepository.findById(anyLong())) + .thenReturn(Optional.of(user)); + when(userRepository.save(any(User.class))).thenReturn(user); + + + // act + User actual = userService.updateUser(id, userUpdateDTO); + + // assert + Assertions.assertNotNull(actual); + Assertions.assertFalse(CollectionUtils.isEmpty(actual.getSkills())); + } +} \ No newline at end of file