How to pass an object to a ModelAttrbiute in MockMVC post? - java

User model:
#Entity
#Table(name="user")
public class User {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
#NotBlank
#Column(name="username")
private String username;
#NotEmpty
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name="user_role", joinColumns = {#JoinColumn(name="user_id")},
inverseJoinColumns = {#JoinColumn(name="role_id")})
private Set<Role> roles;
}
Controller:
#RequestMapping(value = {"/users/edit/{id}"}, method = RequestMethod.POST)
public String editUser(ModelMap model, #Valid #ModelAttribute("user") User user, BindingResult result) {
if(result.hasErrors()) {
return "AddUserView";
}
return "redirect:/users";
}
Test with MockMVC:
#Test
public void performUpdateUserTest() throws Throwable {
mockMvc.perform(post("/users/edit/{id}", user.getId())
.param("username", "User"));
}
Well, fine, I can pass a param username as always using param(). But what should I do with ROLES? This field is a separate object. I can't pass it using param(). Then how is it possible to pass it in the test?
The only way out I found is to create an entity and pass it using .flashAttr():
#Test
public void performUpdateUserTest() throws Throwable {
User user = new User("User", new HashSet<Role>(Arrays.asList(new Role("USER"))));
mockMvc.perform(post("/users/edit/{id}", user.getId())
.flashAttr("user", user));
}
But then, what if I need to test that user can't be updated because of binding error in the ROLES field(ROLES can't be null, and suppose, it was set as null)? Thus, I'm not able to create user(and use it with .flashAttr) already with a binding error as the exception will be thrown. And I still have to pass it separately.

Well, after a long time of searching, I found out that I should add a converter to the MockMVC. What converter is you can read HERE, for instance.
I had it already in my project but didn't realize that it didn't work with MockMVC.
So, you can add the converter to MockMVC like that:
#Autowired
private StringToRoleConverter stringToRoleConverter;
#Before
public void init() {
FormattingConversionService cs = new FormattingConversionService();
cs.addConverter(stringToRoleConverter);
mockMvc = MockMvcBuilders.standaloneSetup(userController)
.setConversionService(cs)
.build();
}
Converter itself:
#Component
public class StringToRoleConverter implements Converter<String, Role> {
#Autowired
private RoleService roleService;
#Override
public Role convert(String id) {
Role role = roleService.findById(Integer.valueOf(id));
return role;
}
}
And then I can add param like that:
mockMvc.perform(post("/users/edit/{id}", user.getId())
.param("roles", "2"))
though I'm passing a string there, it will be converter to Role with the help of Spring converter.

Related

Why is Mongodb #Indexed(unique=true) not working?

My controller:
#PostMapping
public ResponseEntity<UserCreateResponse> createUser(#RequestBody #Valid UserCreateRequest userDto,
BindingResult result)
throws InvalidRequestException {
if (result.hasErrors()) {
throw new InvalidRequestException("Request parameter validation failed");
} else {
return ResponseEntity.ok(userService.createUser(userDto));
}
}
Service:
public UserCreateResponse createUser(UserCreateRequest userDto) {
return convertEntityToDto(userRepository.insert(convertDtoToEntity(userDto)));
}
private User convertDtoToEntity(UserCreateRequest userDto) {
return modelMapper.map(userDto, User.class);
}
private UserCreateResponse convertEntityToDto(User user) {
return modelMapper.map(user, UserCreateResponse.class);
}
And the model is :
#Getter
#Setter
#Document("User")
public class User {
#Id
private String id;
#Indexed(unique = true)
private String userName;
private String name;
private String surname;
private String job;
}
Repository is just a class extending MongoRepository.
When I try to insert 2 User with same userName via postman post request, it is adding 2 exactly same item to db even if I specified #Indexed(unique = true) to userName field. Why does this happen and how can I fix it on Java side without breaking indexing function on the field(I want to index userName field to find faster)

How to get username from mono<user> on spring boot webflux?

I try to make handler and router classes of spring boot webflux. The model class is user class. Codes are
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Document(collection="Users")
public class User {
#Id
private String _id;
#Indexed(unique = true)
private Long id;
#Indexed(unique=true)
private String username;
private String password;
private String email;
private String fullname;
private String role;
}
And below is the handler class of webflux project. In register method, I make the id duplication test codes. But It is totally WRONG.
#Component
public class UserHandler {
#Autowired
private UserReactiveMongoRepository userRepository;
public Mono<ServerResponse> register(ServerRequest request) {
Mono<User> monoUser = request.bodyToMono(User.class);
String id = monoUser.map(u -> u.get_id()).toString();
if(userRepository.existsById(id) == null)
return ServerResponse.ok().build(userRepository.insert(monoUser));
return ServerResponse.ok().build();
}
}
I want to extract the username or id string from Mono of spring webflux.
Any comments will be needed. I am stuck with this part.
One of the things which is wrong here is this like String id = monoUser.map(u -> u.get_id()).toString();. The toString will return you a String like "Mono#13254216541", because you are calling Mono.toString.
One more thing, you shouldn't use the request's data in the body of your function, but in a map or flatMap function.
You could replace it by something like (I'm doing it by head so it might not be 100% syntax corect):
Mono<User> userMono = request.bodyToMono(User.class);//Create a Mono<User>
userMono.map((user) -> { //In the map method, we access to the User object directly
if(user != null && user.getId() != null){
return userRepository.insert(user); // Insert User instead of Mono<User> in your repository
}
return null;
}) //This is still a Mono<User>
.map(insertedUser -> ServerResponse.ok(insertedUser)) //This is a Mono<ServerResponse>
.switchIfEmpty(ServerResponse.ok());
Hope this helps!
a more clean approach would be (i don't like the return of null in the map that others have done) is use the doOnSuccess:
request.bodyToMono(User.class)
.doOnSuccess(user -> return userRepository.insert(user))
.map(user -> ServerResponse.ok(user))
i have left out any error checks but ofc they should be done properly.

how to implement a spring boot controller to return the result of a query as json

I am trying to a write a spring boot controller which can return the result of a native query as json. I will be passing the query as input parameter and the return must be result of the query. Is there a way to do this? I know the http rpc help on this. The query can be anything and the system must accept it and must respond with the result as json.
For example if I pass the request as select * from employee it must respond with result of query as json.
Simply make every function returning:
Map<String, Object>
It will automatically map the object property and value. That means a json object is an instance of Map. If you are managing an array of it, enclose it with a List:
List<Map<String, Object>>
and finally the ResponseEntity becomes:
ResponseEntity<List<Map<String, Object>>>
You could actually use Spring JDBC for that,
Repo
#Repository
public class FooRepo {
#Autowire
private JdbcTemplate jdbcTemplate;
public Object returnDataForQuery(String sql) {
return jdbcTemplate.queryForObject(sql, Object.class); // You could define a proper class if you know the return Type else returning plain object is more then enough
// return jdbcTemplate.queryForList(sql, Object.class) Incase Multiple Data
}
}
Model
public class FooDto {
private String query;
// Getter, Setter & No Args Constructor (or) Lombok
}
Controller
#Autowire
private FooRepo fooRepo;
#PostMapping(value = "/postData", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity postData(#RequestBody FooDto foo) {
return ResponseEntity.ok(fooRepo.returnDataForQuery(foo.getQuery);
}
This is just a overview, you could bend it.As for your result output concern you ResponseEntity will take care of it
SpringBoot
//Controller Class
#RestController
#RequestMapping("/employee")
public class EmployeeController {
#Autowired
private EmployeeService employeeService;
#GetMapping("/all")
public List<Employee> getAllEmplpyee() {
logger.info("get All Employeee");
return employeeService.getAllEmployeeService();
}
}
//ServiceImpl
#Service
public class EmployeeService {
private static final Logger logger = LoggerFactory.getLogger(EmployeeService.class);
#Autowired
private EmployeeRepository employeeRepository;
public List<Employee> getAllEmployeeService() {
logger.info(getClass().getName()," invked getAllEmployee");
List<Employee> empBo = employeeRepository.findAll();
return copyPropertiesValues(empBo);
}
}
//DAO
#Component
public interface EmployeeRepository extends JpaRepository<Employee, String>{
}
//Model
#Entity
#Table(name = "employees")
public class Employee {
#Id
#Column(name = "employeeNumber",nullable=false)
private String employeeNumber;
#Column(nullable=false)
private String lastName;
#Column(nullable=false)
private String firstName;
#Column(nullable=false)
private String extension;
#Column(nullable=false)
private String email;
#Column( nullable=false)
private String officeCode;
#Column(nullable=false)
private String reportsTo;
#Column(nullable=false)
private String jobTitle;
//GETTER SETTER
}
//application.properties
spring.jpa.hibernate.ddl-auto=update
spring.jpa.open-in-view=true
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.type.descriptor.sql=trace
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=****

Facing issue on service side validation

I'm trying to implement the server side validation using spring. but its not validating. Here is my code sample.
#RestController
#RequestMapping("/api/v1/note")
public class NoteController {
#Autowired
private final NoteService noteService;
#PostMapping
public ResponseEntity<String> create(#Valid #RequestBody final NoteDto noteDto){
noteService.create(noteDto);
return new ResponseEntity<>("sucess", HttpStatus.CREATED);
}
}
POJO..
#Data
#JsonInclude(value = Include.NON_NULL)
public class NoteDto {
#NotEmpty(message = "Building No can't be empty!")
private String buildingNo;
private String buildingName;
#NotEmpty(message = "Street can't be empty!")
}
What am missing here
#Valid annotation that triggers validations on the NoteDto (in this case #NotNull and #Future). These annotations could come from different JSR-303 providers (e.g, Hibernate, Spring..etc).
Example
static class NoteDto {
#NotNull #Future
private Date date;
}
And Remove final.

SpringBoot JPA throws exception while making post request

Im making a small application where i can save user details using spring-boot. i created the entities and their corresponding repositories. When ever i make a post request to add a user the id of the user object which is null at the point of saving to the data base.This id is auto generated(Auto Increment) in MySQL. From the POST request i get 3 fields which are username,email,password. The User class contains fields id,username,email,password. I've added the annotations
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Integer id;
for the id field. an the constructors are
public User() { }
public User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
This is the error im getting.
The debugging process
my userService class
#Service
public class UserService implements UserServiceInterface {
#Autowired(required = true)
private UserRepository userrepository;
#Override
public User CreateNewUser(User user) {
return userrepository.save(user);
}
}
my userController class
#RestController
public class UserController {
UserService us = new UserService();
#RequestMapping(value ="/user",method = RequestMethod.POST)
public void RegisterUser(
#RequestParam(value="username") String username,
#RequestParam(value="email") String email,
#RequestParam(value="password") String password){
us.CreateNewUser(new User(username,email,password));
}
}
Any reason why i cant POST to save data to database? how to overcome this?
After digging through the code i found out the error. by creating a new instance of UserService us = new UserService(); this is not managed by Spring (Spring doesn't know about it and cannot inject UserRepository - this causes NullPointerException). there of instead of creting new instace it should extends the UserService class in this example.

Categories

Resources