Springboot does not save user properly using JpaRepository - spring-boot

I couldn't use userRepo to saveUser properly, the mysql query showed wrong order of insertion, therefore not working. Here's my UserRepository:
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findUserByUsername(String username);
}
Here's my user service:
#Service
public class UserServiceImpl implements IUserService, UserDetailsService {
private Logger logger = LogManager.getLogger(UserServiceImpl.class);
#Autowired
private UserRepository userRepo;
#Autowired
private BCryptPasswordEncoder encoder;
//field name not in correct order, can not use it
#Override
public Integer saveUser(User user) {
String passwd = user.getPassword();
String encodedPasswod = encoder.encode(passwd);
logger.log(Level.ERROR, "passwd:" + encodedPasswod);
user.setPassword(encodedPasswod);
ArrayList<String> roles = new ArrayList<String>();
roles.add("Member");
user.setRoles(roles);
user = userRepo.save(user);
return user.getUserid();
}
public UserServiceImpl(UserRepository userRepository) {
this.userRepo = userRepository;
}
...
Someone can help? So frustrated with springboot.
M.

Look like you missing #Transactional on the saveUser method.

Related

Test Result showing null while performing jUnit5 Test

I am writing a jUnit5 test to test my getUserByEmail(String email) using Mockito and when running the test, it is failing.
#SpringBootTest
#ExtendWith(MockitoExtension.class)
class UserServiceTest {
#MockBean
private UsersRepository userRepository;
#Autowired
private UserService userService;
#Test
void check_email_existInDB_thenReturn_True() {
// given - precondition or setup
UserDetails userdetails2 = new UserDetails(101L, "Anthony Ji", "anthony#gmail.com", "password2");
userRepository.save(userdetails2);
// when - action or behaviour that we are going test
when(userRepository.save(userdetails2)).thenReturn(userdetails2);
// then - verify the result or output using assert statements
assertEquals(userdetails2.getEmail(), userService.getUserByEmail("anthony#gmail.com"));
//assertTrue(userService.getUserByEmail(userdetails2.getEmail()));
}
}
This is my interface with extending of JPARepository
public interface UsersRepository extends JpaRepository<UserDetails, Long>{
}
This is my service level class
#Service
public interface UserService {
public Optional<UserDetails> getUserByEmail(String email);
public UserDetails saveUserDetails(UserDetails userDetails);
}
And this is the serviceImpl Class..
#Service
public class UserServiceImpl implements UserService{
#Autowired
private UsersRepository userRepository;
#Override
public Optional<UserDetails> getUserByEmail(String email) {
List<UserDetails> allUsers = this.userRepository.findAll();
Optional<UserDetails> userInfo = allUsers.stream().filter(user -> user.getEmail().equalsIgnoreCase(email)).findAny();
return userInfo;
}
#Override
public UserDetails saveUserDetails(UserDetails userDetails) {
UserDetails savedUserDetails = this.userRepository.save(userDetails);
return savedUserDetails;
}
}
and finally, this is my controller class while using jersey framework.
#Path(value = "/user")
public class RegistrationResources {
private static Logger logger = LoggerFactory.getLogger(RegistrationResources.class);
#Autowired
private UserService userService;
#POST
#Path("/registration")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response registerUser(UserDetails userDetails, #Context UriInfo uriInfo) {
logger.info("Inside the registration resource and registering the user");
boolean isEmailExists = isEmailExists(userDetails.getEmail());
if(isEmailExists) {
logger.info("Email Already Registered");
return Response.status(404).entity("Email Address already registered").build();
}
else {
this.userService.saveUserDetails(userDetails);
logger.info("User successfully registered");
return Response.status(200).entity("User successfully registered").build();
}
}
public boolean isEmailExists(String email) {
UserDetails userByEmail = this.userService.getUserByEmail(email).orElse(null);
if(userByEmail == null) {
return false;
}
return true;
}
I tried as above explained in the code.

How can I Integrate SpringSecuirty to My SpringBootTest?

I'm trying to test a comment_post method.
Comment has many - to - one relationship with User Entity which comes from Spring Security.
I connected this relationship by using Principal.
I think I made it working properly, but having trouble applying it to test.
Problem is that Comment Posting method gets user by finding User in Repository using Principal's email attribute, So I need to apply SecurityContext to test,
but I have no idea how to apply this function to test.
By Searching, I found out that I can make SpringSecurityContext by #WithSecurityContext
annotation, so I'm trying to apply it but having this error
java.lang.RuntimeException: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'springboot.web.CommentsApiControllerTest$WithUserDetailsSecurityContextFactory': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'springboot.web.CommentsApiControllerTest' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
I'm not even sure that my approach is correct.
tbh, I kind of feel lost, maybe it's because I'm new to SpringBoot, also Security.
Here's my codes.
CommentService
#RequiredArgsConstructor
#Service
public class CommentService {
private final CommentRepository commentRepository;
private final PostsRepository postsRepository;
private final UserDetailService userDetailService;
#Transactional
public Long commentSave(CommentSaveRequestDto requestDto, Long id) {
Posts post = postsRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("해당 게시글이 존재하지 않습니다"));
requestDto.setPosts(post);
User user = userDetailService.returnUser();
requestDto.setUser(user);
return commentRepository.save(requestDto.toEntity()).getId();
}
`
UserDetailService
#RequiredArgsConstructor
#Service
public class UserDetailService {
private final UserRepository userRepository;
public User returnUser() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String userName;
if (principal instanceof UserDetails) {
userName = ((UserDetails) principal).getUsername();
} else {
userName = principal.toString();
}
int start = userName.indexOf("email")+6;
int end = userName.indexOf(".com,")+4;
String email = userName.substring(start, end);
User user = userRepository.findByEmail(email).orElse(null);
return user;
}
CommentSaveRequestDto
#Data
#NoArgsConstructor
#Builder
#AllArgsConstructor
public class CommentSaveRequestDto {
private String comment;
private Posts posts;
private User user;
/* Dto -> Entity */
public Comment toEntity() {
return Comment.builder()
.comment(comment)
.posts(posts)
.user(user)
.build();
}
}
And here is my CommentsApiControllrTest
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#Transactional
public class CommentsApiControllerTest {
#LocalServerPort
private int port;
#Autowired
private PostsRepository postsRepository;
#Autowired
private CommentRepository commentRepository;
#Autowired
private UserRepository userRepository;
#Autowired
private PostsService postsService;
#Autowired
private CommentService commentService;
#Autowired
private UserDetailService userDetailsService;
#Autowired
private WebApplicationContext context;
#Autowired ObjectMapper objectMapper;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.apply(sharedHttpSession())
.build();
}
#Retention(RetentionPolicy.RUNTIME)
#WithSecurityContext(factory = WithUserDetailsSecurityContextFactory.class)
public #interface WithMockCustomUser {
String name() default "testName";
String email() default "testemail#gmail.com";
Role role() default Role.USER;
}
final class WithUserDetailsSecurityContextFactory implements WithSecurityContextFactory<WithUserDetails> {
private final UserDetailsService userDetailsService;
#Autowired
public WithUserDetailsSecurityContextFactory(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public org.springframework.security.core.context.SecurityContext createSecurityContext(WithUserDetails withUser) {
String username = withUser.value();
Assert.hasLength(username, "value() must be non-empty String");
UserDetails principal = userDetailsService.loadUserByUsername(username);
Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(), principal.getAuthorities());
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
return context;
}
}
#After
public void tearDown() throws Exception {
postsRepository.deleteAll();
commentRepository.deleteAll();
}
#Test
#WithMockCustomUser
#Transactional // 프록시 객체에 실제 데이터를 불러올 수 있게 영속성 컨텍스트에서 관리
public void comment_등록() throws Exception {
// given
String title = "title";
String content = "content";
User user = userRepository.save(User.builder()
.name("name")
.email("fake#naver.com")
.picture("fakePic.com")
.role(Role.USER)
.build());
PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
.title(title)
.content(content)
.user(user)
.build();
postsRepository.save(requestDto.toEntity());
String comment = "comment";
Posts posts = postsRepository.findAll().get(0);
CommentSaveRequestDto saveRequestDto = CommentSaveRequestDto.builder()
.comment(comment)
.posts(posts)
.build();
Long id = posts.getId();
String url = "http://localhost:"+ port + "/api/posts/" + id + "/comments";
//when
mvc.perform(post(url)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(objectMapper.writeValueAsString(saveRequestDto)))
.andExpect(status().isOk())
.andDo(print());
}
All I want is to make a mock Security User in test, so that
User user = userDetailService.returnUser();
this line in CommentService don't make any error.
Just a little tip would be really helpful to me.
Thank you in advance.

I have a problem with testing adding user with password encoder

I have a problem when testing a method with using passwordencoder:
Cannot invoke "org.springframework.security.crypto.password.PasswordEncoder.encode(java.lang.CharSequence)" because the return value of "com.store.restAPI.user.UserConfig.passwordEncoder()" is null`
Thats my test class method:
#ExtendWith(MockitoExtension.class)
class UserServiceTest {
private UserService underTest;
#Mock
private UserRepository userRepository;
#Mock
private UserConfig userConfig;
#BeforeEach
void setUp(){
underTest = new UserService(userRepository, userConfig);
}
#Test
void itShouldFindAllUsers() {
//when
underTest.findAll();
//then
verify(userRepository).findAll();
}
#Test
void addNewUser() {
//given
User expected = new User(
"mateusz#gmail.com",
"123"
);
//when
underTest.addNewUser(expected);
//then
ArgumentCaptor<User> userArgumentCaptor =
ArgumentCaptor.forClass(User.class);
verify(userRepository).save(userArgumentCaptor.capture());
User capturedUser = userArgumentCaptor.getValue();
assertThat(capturedUser).isEqualTo(expected);
}
#Test
#Disabled
void loginUser() {
}
}
And thats UserService method that i want to test:
#Service
public class UserService {
private final UserRepository userRepository;
private final UserConfig userConfig;
#Autowired
public UserService(UserRepository userRepository, UserConfig userConfig) {
this.userRepository = userRepository;
this.userConfig = userConfig;
}
public List<User> findAll() {
return userRepository.findAll();
}
public void addNewUser(User user) {
Optional<User> userOptional = userRepository.findUserByEmail(user.getEmail());
if(userOptional.isPresent()){
throw new IllegalStateException("email taken");
}
String hashedPassword = userConfig.passwordEncoder().encode(user.getPassword());
user.setPassword(hashedPassword);
userRepository.save(user);
}
public void loginUser(User user){
Optional<User> userOptional = userRepository.findUserByEmail(user.getEmail());
if(userOptional.isEmpty()){
throw new IllegalStateException("no account under that email");
}
else
if(!userConfig.passwordEncoder().matches(user.getPassword(),
userOptional.get().getPassword())){
throw new IllegalStateException("wrong password");
}
//!userOptional.get().getPassword().equals(user.getPassword())
}
}
Password encoder is a bean in class UserConfig.
#Configuration
public class UserConfig {
#Bean
CommandLineRunner commandLineRunnerUser(UserRepository repository) {
return args -> {
User iza = new User(
"iza#gmail.com",
"$2a$10$U87IFlm9DYXRITUSnfdfDuknz8ijJCcK9UVR4D4kUDu7w13zPuURK"
);
User andrzej = new User(
"andrzej#gmail.com",
"$2a$10$fmYOxyvWBr47wAg1m/ryy.G4J1PbT2LRj6m7oENkBtEsGocansE9G"
);
User tomek = new User(
"tomek#gmail.com",
"$2a$10$chrySvbZSZcje4r3Q0PZv.FrO6/k2WvM42GX3x2EmySZc/dAA2glC"
);
repository.saveAll(
List.of(iza,andrzej,tomek)
);
};
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Do i need to create another method with password encoder inside my test class? I don't know what am i doing wrong. Why does it say that result is null?
Does someone know what am i doing wrong?
In your UserServiceTest, you're currently mocking UserConfig. A mock is basically an implementation that has no behavior at all. This means that userConfig.passwordEncoder() will return null in your test.
To solve this, you have to tell Mockito what the behavior is of your UserConfig class. For example:
Mockito.when(userConfig.passwordEncoder()).thenReturn(new BCryptPasswordEncoder());
This also allows you to use a different (eg. a dummy) password encoder in your unit tests.
Another suggestion, unrelated to your question, is that you can directly autowire PasswordEncoder in your UserService in stead of autowiring UserConfig. This works because you annotated UserConfig with #Configuration and UserConfig.passwordEncoder() with #Bean:
#Service
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder; // Change this
// And change the constructor parameter
#Autowired
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder; // And this
}
// The rest of your code...
}

Inferred type 'S' for type parameter 'S' is not within its bound; should extend

When calling the UserRepository from my UserServiceImpl, I get this error:
"Inferred type 'S' for type parameter 'S' is not within its bound; should extend 'com.dev.backendproductmanagement.entity.UserEntity'
Any ideas?
#Service
#RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final ObjectMapper objectMapper;
#Override
public User saveUser(final User user) {
final UserEntity userEntity = userRepository.save(user);
return objectMapper.convertValue(userEntity, User.class);
}
}
UserRepository:
#Repository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
// findBy -> Username
Optional<UserEntity> findByUserName(String userName);
}
You have to replace UserEntity to User in Repository like
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
// findBy -> Username
Optional<UserEntity> findByUserName(String userName);
}
If you are intending to support polymorphic JpaRepository types, you have to declare the latter as is:
#Repository
public interface UserEntityRepository<T extends UserEntity> extends JpaRepository<T, Long> {
// findBy -> Username
Optional<T> findByUserName(String userName);
}
Given above base repository type, you can declare specializations as follows given User extends UserEntity:
#Repository
public interface UserRepository extends UserEntityRepository<User> {
}
I solved the problem by using object mapping like so:
UserServiceImpl:
#Override
public User saveUser(final User user) {
final UserEntity userEntity = objectMapper.convertValue(user, UserEntity.class);
userRepository.save(userEntity);
return objectMapper.convertValue(userEntity, User.class);
}
1- look at her in Service:
final UserEntity userEntity = userRepository.save(user);
you tell to useRepository to save an object of type "user".
2- But in your Repository you have "UserEntity":
public interface UserRepository extends JpaRepository<UserEntity, Long>
So change :
public interface UserRepository extends JpaRepository<UserEntity, Long>
to:
public interface UserRepository extends JpaRepository<User, Long>
or the opposite.
change this:
#Override
public User saveUser(final User user) {
final UserEntity userEntity = userRepository.save(user);
return objectMapper.convertValue(userEntity, User.class);
}
to:
#Override
public userEntitysaveUser(final userEntity user) {
final UserEntity userEntity = userRepository.save(user);
return objectMapper.convertValue(userEntity, User.class);
}

RestController with few classes

I have a few classes in the JPA Base made with the use of SpringData and SpringBoot. I would like to create a controller which shows the rest out of a few classes and not only one. Sadly, I can't find any examples.
This is my controller:
#RestController
#RequestMapping("/user")
public class UserController {
private final UserRepository userRepository;
#Autowired
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
#RequestMapping(method = RequestMethod.POST)//add
public void addUser(#RequestBody User user) {
userRepository.save(user);
}
#RequestMapping(method = RequestMethod.GET)
public List<User> getUser(){
return userRepository.findAll();
}
}

Resources