Consuming Restful api in Spring MVC - java

I am new to Spring MVC. I need to know how to consume RESTful api in UI.
And also I need to know can we use the api for the same app or we shall create new app to consume these api produced from REST. I have built a REST api in my project and I used the api in the same project with following code. But it didnt work.
RestClient.java
package com.spring.template;
import org.springframework.web.client.RestTemplate;
import com.spring.model.Employee;
public class RestClient {
public static void main(String[] args) {
try {
RestTemplate restTemplate = new RestTemplate();
final String base_url = "http://localhost:8080/SpringWebSevices/";
Employee employee = restTemplate.getForObject(base_url, Employee.class, 200);
System.out.println("Id:"+employee.getEmpid());
}
catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
}
}
EmployeeController.java
package com.spring.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.spring.model.Employee;
import com.spring.service.EmployeeService;
#RestController
public class EmployeeController {
#Autowired
EmployeeService employeeService;
#RequestMapping(value ="/", method = RequestMethod.GET, produces ="application/json")
public ResponseEntity<List<Employee>> employees() {
HttpHeaders headers = new HttpHeaders();
List<Employee> employee = employeeService.getEmployees();
if(employee == null) {
return new ResponseEntity<List<Employee>>(HttpStatus.NOT_FOUND);
}
headers.add("Number of records found:", String.valueOf(employee.size()));
return new ResponseEntity<List<Employee>>(employee, HttpStatus.OK);
}
#RequestMapping(value="/employee/add", method = RequestMethod.POST , produces ="application/json")
public ResponseEntity<Employee> addEmployee(#RequestBody Employee employee) {
HttpHeaders headers = new HttpHeaders();
if(employee == null) {
return new ResponseEntity<Employee>(HttpStatus.BAD_REQUEST);
}
employeeService.createEmployee(employee);
headers.add("Added employee id:", String.valueOf(employee.getEmpid()));
return new ResponseEntity<Employee>(employee, headers, HttpStatus.CREATED);
}
#RequestMapping(value = "/employee/edit/{id}",method=RequestMethod.PUT)
public ResponseEntity<Employee> editEmployee(#PathVariable("id") int empid,#RequestBody Employee employee) {
HttpHeaders headers = new HttpHeaders();
Employee isExist = employeeService.getEmployee(empid);
if(isExist == null) {
return new ResponseEntity<Employee>(HttpStatus.NOT_FOUND);
} else if(employee == null) {
return new ResponseEntity<Employee>(HttpStatus.BAD_GATEWAY);
}
employeeService.updateEmployee(employee);
headers.add("Employee updated:", String.valueOf(employee.getEmpid()));
return new ResponseEntity<Employee>(employee,headers,HttpStatus.OK);
}
#RequestMapping(value = "/employee/delete/{id}", method =RequestMethod.DELETE)
public ResponseEntity<Employee> deleteEmployee(#PathVariable("id") int empid) {
HttpHeaders headers = new HttpHeaders();
Employee employee = employeeService.getEmployee(empid);
if(employee == null) {
return new ResponseEntity<Employee>(HttpStatus.NOT_FOUND);
}
employeeService.deleteEmployee(empid);
headers.add("Employee deleted:", String.valueOf(empid));
return new ResponseEntity<Employee>(employee, headers, HttpStatus.NO_CONTENT);
}
}
This is the error i got:
nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.spring.model.Employee out of START_ARRAY token

com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of com.spring.model.Employee out of START_ARRAY token
This looks like your sending a list of Employee to some method that accepts a single Employee as a parameter. Possibilities:
addEmployee
editEmployee

Your controller is returning List<Employee>, while you are trying to assign the result to a single Employee object in the line
Employee employee = restTemplate.getForObject(base_url, Employee.class, 200);
You are facing type incompatibilities, you could try
ResponseEntity<? extends ArrayList<Employee>> responseEntity = restTemplate.getForEntity(base_url, (Class<? extends ArrayList<Employee>)ArrayList.class, 200);
I haven't tested it yet but it should work.

Related

Why is my custom exception not returning in JSON format?

I have connected a Spring applications to a React front-end where I need to display my custom exceptions. The custom exceptions work perfectly in Spring, but the front-end (React) only receives the error code (417) and nothing else.
I have determined that the problem is that the exception is not being returned in JSON format because the error message is displayed in its entirety when I use Postman, but not in JSON format.
My research has shown that since I am using a #RestController (for my main controller) and #ControllerAdvice (for my custom exception handler) that it should be returning in JSON format. I also tried adding a ResponseBody bean to the specific function but that did not help either.
Controller.java
package com.chess.controller;
import com.chess.board.*;
import com.chess.gameflow.Game;
import com.chess.gameflow.Move;
import com.chess.gameflow.Player;
import com.chess.gameflow.Status;
import com.chess.models.requests.BoardRequest;
import com.chess.models.requests.PlayerRequest;
import com.chess.models.requests.StatusRequest;
import com.chess.models.responses.MovesResponse;
import com.chess.models.responses.Response;
import com.chess.models.responses.StatusResponse;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#CrossOrigin(origins= "http://localhost:3000", maxAge=7200)
#RestController
#RequestMapping("/game")
public class Controller {
Game game;
Board board = Board.boardConstructor();
#PostMapping("/players")
public List<Response> createPlayer(#RequestBody PlayerRequest request){
game = new Game(request.getName1(), request.getName2());
List<Response> returnValue = board.returnBoard();
Player player1= Game.players[0];
StatusResponse status = new StatusResponse(Status.isActive(), Status.isCheck(), player1);
returnValue.add(status);
return returnValue;
}
#PostMapping
public List<Response> makeMove(#RequestBody BoardRequest boardRequest){
StatusResponse status = Game.run(boardRequest);
List<Response> returnValue = board.returnBoard();
returnValue.add(status);
return returnValue;
}
#PostMapping("/end")
public StatusResponse endGame(#RequestBody StatusRequest statusRequest){
Status.setActive(false);
Board board = Board.boardConstructor();
board.generateBoard();
if (statusRequest.isForfeit()){
StatusResponse statusResponse = new StatusResponse(statusRequest.getPlayerName() + " declares defeat! Game Over!");
return statusResponse;
}
StatusResponse statusResponse = new StatusResponse("We have a draw! Good Game!");
return statusResponse;
}
#GetMapping("/moves")
public MovesResponse displayMoves(){
MovesResponse movesResponse = new MovesResponse(Move.returnMoveMessages());
return movesResponse;
}
}
CustomExceptionsHandler.java
package com.chess.exceptions;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
#ControllerAdvice
public class CustomExceptionsHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = InvalidMoveException.class)
#ResponseBody //this line shouldn't be necessary as I am using a RestController but I added it anyways in one of my futile attempts and I don't think it should hurt
protected ResponseEntity<Object> resolveInvalidMove(InvalidMoveException e, WebRequest req) throws Exception {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.EXPECTATION_FAILED.value(),
HttpStatus.EXPECTATION_FAILED.getReasonPhrase(),
e.getMessage(),
req.getDescription(true));
return handleExceptionInternal(e, errorResponse.toString(), new HttpHeaders(), HttpStatus.EXPECTATION_FAILED, req);
#ExceptionHandler(value = MustDefeatCheckException.class)
protected ResponseEntity<Object> resolveCheckStatus(MustDefeatCheckException e, WebRequest req){
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.EXPECTATION_FAILED.value(),
HttpStatus.EXPECTATION_FAILED.getReasonPhrase(),
e.getMessage(),
req.getDescription(true));
return handleExceptionInternal(e, errorResponse, new HttpHeaders(), HttpStatus.EXPECTATION_FAILED, req);
}
}
ErrorResponse.java
package com.chess.exceptions;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "error")
public class ErrorResponse {
private int status;
private String errReason;
private String errMessage;
private String path;
public ErrorResponse(int status, String errReason, String errMessage, String path) {
this.status = status;
this.errReason = errReason;
this.errMessage = errMessage;
this.path = path;
}
#Override
public String toString(){
return "Status Code: " + status + " " + errReason + " Message: " + errMessage + " at " + path;
}
}
InvalidMoveException.java
package com.chess.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
#ResponseStatus(value = HttpStatus.EXPECTATION_FAILED, reason="Invalid Move")
public class InvalidMoveException extends RuntimeException {
public InvalidMoveException(String msg){ super(msg); }
}
I solved half the problem. Instead of returning a ResponseEntity with handleExceptionInternal, I was able to return the ErrorResponse itself by making it a child of Response which is the father to all my regular responses.
So now my CustomExceptionsHandler.java looks like this-
#RestControllerAdvice
public class CustomExceptionsHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = InvalidMoveException.class)
#ResponseStatus(HttpStatus.EXPECTATION_FAILED)
public ErrorResponse resolveInvalidMove(InvalidMoveException e, WebRequest req) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.EXPECTATION_FAILED.value(),
HttpStatus.EXPECTATION_FAILED.getReasonPhrase(),
e.getMessage(),
req.getDescription(true));
return errorResponse;
}
}
And I am getting the exception in JSON format when I use Postman. However, my error in
React is unchanged. I am still only getting the status code and no other information.
Board.js
DataService.makeMove(move)
.then(res => {
//console.log(res.data);
setIsWhite((prev) => !prev);
props.setTheBoard(res.data);
setStatus(res.data[64]);
updateMovesList();
})
.catch(err => {
console.log(err)
console.log(err.errMessage)
console.log(err.message)
console.log(err.status)
console.log(err.errReason)
})
DataService.js
makeMove(move){
return axios.post(`${url}`, move);
}
I always thought catching errors was very simple but apparently I am missing something

How can I use #WebMvcTest for Unit Test POST method?

I'm running unit tests with Spring Boot and Mockito and I'm testing RESTful services. When I try to test the GET method it works successfully but when I try to test the POST method it fails. What should I do to resolve this problem? Thanks in advance!
This is the code for the REST Controller:
package com.dgs.restfultesting.controller;
import java.net.URI;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import com.dgs.restfultesting.business.ItemBusinessService;
import com.dgs.restfultesting.model.Item;
#RestController
public class ItemController {
#Autowired
private ItemBusinessService businessService;
#GetMapping("/all-items-from-database")
public List<Item> retrieveAllItems() {
return businessService.retrieveAllItems();
}
#PostMapping("/items")
public Item addItem(#RequestBody Item item) {
Item savedItem = businessService.addAnItem(item);
return savedItem;
}
}
Business Layer:
package com.dgs.restfultesting.business;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.dgs.restfultesting.data.ItemRepository;
import com.dgs.restfultesting.model.Item;
#Component
public class ItemBusinessService {
#Autowired
private ItemRepository repository;
public Item retrieveHardcodedItem() {
return new Item(1, "Book", 10, 100);
}
public List<Item> retrieveAllItems() {
List<Item> items = repository.findAll();
for (Item item : items) {
item.setValue(item.getPrice() * item.getQuantity());
}
return items;
}
public Item addAnItem(Item item) {
return repository.save(item);
}
}
ItemControllerTest:
package com.dgs.restfultesting.controller;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.Arrays;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import com.dgs.restfultesting.business.ItemBusinessService;
import com.dgs.restfultesting.model.Item;
#RunWith(SpringRunner.class)
#WebMvcTest(ItemController.class)
public class ItemControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private ItemBusinessService businessService;
#Test
public void retrieveAllItems_basic() throws Exception {
when(businessService.retrieveAllItems()).thenReturn(
Arrays.asList(new Item(2, "iPhone", 1000, 10),
new Item(3, "Huawei", 500, 17)));
RequestBuilder request = MockMvcRequestBuilders
.get("/all-items-from-database")
.accept(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(request)
.andExpect(status().isOk())
.andExpect(content().json("[{id:2, name:iPhone, price:1000}, {id:3, name:Huawei, price:500}]")) // This will return an array back, so this data should be within an array
.andReturn();
}
#Test
public void createItem() throws Exception {
RequestBuilder request = MockMvcRequestBuilders
.post("/items")
.accept(MediaType.APPLICATION_JSON)
.content("{\"id\":1,\"name\":\"Book\",\"price\":10,\"quantity\":100}")
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(request)
.andExpect(status().isCreated())
.andExpect(header().string("location", containsString("/item/")))
.andReturn();
}
}
There is no problem to test the retrieveAllItems_basic() method, but when I try to run a JUnit test for createItem() method, it doesn't work and I get this: java.lang.AssertionError: Status expected:<201> but was:<200>
And this is what I receive in the Console:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /items
Parameters = {}
Headers = {Content-Type=[application/json], Accept=[application/json]}
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.dgs.restfultesting.controller.ItemController
Method = public com.dgs.restfultesting.model.Item com.dgs.restfultesting.controller.ItemController.addItem(com.dgs.restfultesting.model.Item)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
2018-10-07 17:53:51.457 INFO 55300 --- [ Thread-3] o.s.w.c.s.GenericWebApplicationContext : Closing org.springframework.web.context.support.GenericWebApplicationContext#71075444: startup date [Sun Oct 07 17:53:48 EEST 2018]; root of context hierarchy
Update -----------------------------
I try to set the location like this: item/id.
This is the code for the controller:
#PostMapping("/items")
public ResponseEntity<Object> addItem(#RequestBody Item item) {
Item savedItem = businessService.addAnItem(item);
HttpHeaders httpHeaders = new HttpHeaders();
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.newInstance();
UriComponents uriComponents =
uriComponentsBuilder.path("/item/{id}").buildAndExpand(savedItem.getId());
httpHeaders.setLocation(uriComponents.toUri());
return new ResponseEntity<>(savedItem, httpHeaders, HttpStatus.CREATED);
}
This is the code for test:
#Test
public void createItem() throws Exception {
RequestBuilder request = MockMvcRequestBuilders
.post("/items")
.accept(MediaType.APPLICATION_JSON)
.content("{\"id\":1,\"name\":\"Book\",\"price\":10,\"quantity\":100}")
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(request)
.andExpect(status().isCreated())
.andExpect(header().string("location", containsString("/item/1")))
.andReturn();
}
When I run the JUnit test for createItem() method I get this error: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
Return 201 from your controller: As your assertion test is expecting 201 by using created status but your controller is returning 200(OK).
#PostMapping("/items")
public ResponseEntity<?> addItem(#RequestBody Item item) {
Item savedItem = itemBusinessService.addAnItem(item);
return new ResponseEntity<>(savedItem, HttpStatus.CREATED);
}
Or modify your test to check status OK(200). Update your test if you don't want to assert "location".
#Test
public void createItem() throws Exception {
RequestBuilder request = MockMvcRequestBuilders
.post("/items")
.accept(MediaType.APPLICATION_JSON)
.content("{\"id\":1,\"name\":\"Book\",\"price\":10,\"quantity\":100}")
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(request)
.andExpect(status().isOk()).andReturn();
}
UPDATE--Allow location header in response
If you want "location" to return from header, then modify your controller and test case below to check location too in header.
STEP 1: In your controller's add item method, add location uri and return.
#PostMapping("/items")
public ResponseEntity<?> addItem(#RequestBody Item item) {
Item savedItem = businessService.addAnItem(item);
HttpHeaders httpHeaders = new HttpHeaders();
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.newInstance();
UriComponents uriComponents =
uriComponentsBuilder.path("/item/").buildAndExpand("/item/");
httpHeaders.setLocation(uriComponents.toUri());
return new ResponseEntity<>(savedItem, httpHeaders, HttpStatus.CREATED);
}
STEP 2: Now your test will assert "location" as you expected.
#Test
public void createItem() throws Exception {
RequestBuilder request = MockMvcRequestBuilders
.post("/items")
.accept(MediaType.APPLICATION_JSON)
.content("{\"id\":1,\"name\":\"Book\",\"price\":10,\"quantity\":100}")
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(request)
.andExpect(status().isCreated())
.andExpect(header().string("location", containsString("/item/")))
.andReturn();
}
first of all I do not see in your createItem test the mock program part let's say
Item item = new Item();
Item newItem = new Item();
when(businessService.addAnItem(item)).thenReturn(newItem);
and in your controller I do not see the Location header. Probably a code like below should be better:
#PostMapping("/items")
public ResponseEntity<?> addItem(#RequestBody Item item) {
Item savedItem = itemBusinessService.addAnItem(item);
return ResponseEntity.created(UriComponentsBuilder.fromHttpUrl("http://yourserver/item"));
}
I hope that this can help you

SpringBoot Elastisearch RestHighLevelClient Java Search-API

I'm following this Tutorial and everything works fine.
Except I wan to add some extra search functionalities.
I have the following document is my Elasticsearch 6.1 index:
{
"author": "georges",
"price": 99.1,
"id": "06e68109-504a-44d6-bf2e-0debb12c984d",
"title": "Java Always"
}
My Spring Boot app runs on port 8080. I know how to insert data by using following API with postman : 127.0.0.1:8080/books and also how to get the book with its ID 127.0.0.1:8080/books/06e68109-504a-44d6-bf2e-0debb12c984d thanks to the GET request provided by E-S Java High Level Rest API:
//This works perfectly thank you
#Repository
public class BookDao {
private final String INDEX = "bookdata";
private final String TYPE = "books";
private RestHighLevelClient restHighLevelClient;
...
public Map<String, Object> getBookById(String id) {
GetRequest getRequest = new GetRequest(INDEX, TYPE, id);
GetResponse getResponse = null;
try {
getResponse = restHighLevelClient.get(getRequest);
} catch (java.io.IOException e) {
e.getLocalizedMessage();
}
Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
return sourceAsMap;
}
Question is: How can I search this book by it's author ?
I've tried exactly the same implementation but it doesnt work because GetRequest only gets documents by Document id
public Map<String, Object> getBookByAuthor(String author) throws IOException {
GetRequest getRequest = new GetRequest(INDEX, TYPE, author);
GetResponse getResponse = null;
try {
getResponse = restHighLevelClient.get(getRequest);
} catch (java.io.IOException e) {
e.getLocalizedMessage();
}
Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
return sourceAsMap;
}
My controller:
import java.util.Map;
import org.springframework.web.bind.annotation.DeleteMapping;
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.RestController;
import com.gvh.es.rest.es6rest.dao.BookDao;
import com.gvh.es.rest.es6rest.model.Book;
#RestController
#RequestMapping("/books")
public class BookController {
private BookDao bookDao;
public BookController(BookDao bookDao) {
this.bookDao = bookDao;
}
#GetMapping("/{id}")
public Map<String, Object> getBookById(#PathVariable String id){
return bookDao.getBookById(id);
}
#PostMapping
public Book insertBook(#RequestBody Book book) throws Exception {
return bookDao.insertBook(book);
}
#PutMapping("/{id}")
public Map<String, Object> updateBookById(#RequestBody Book book, #PathVariable String id) {
return bookDao.updateBookById(id, book);
}
#DeleteMapping("/{id}")
public void deleteBookById(#PathVariable String id) {
bookDao.deleteBookById(id);
}
}
I encountered the problem too,and this is my solution to the problem. It works with my code.
Change the username and password, and I think it may work.
private RestHighLevelClient buildClient() {
try {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,new UsernamePasswordCredentials("username", "password"));
RestClientBuilder builder = RestClient.builder(
new HttpHost("localhost", 9200, "http")).setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
#Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
}
});
restHighLevelClient = new RestHighLevelClient(builder);
} catch (Exception e) {
logger.error(e.getMessage());
}
return restHighLevelClient;
}

Spring Boot: MockMvc returning strange responses

I have the following controller:
RestApiController.java
package com.spring.ocr.rest.restconsume.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;
import com.spring.ocr.rest.restconsume.model.User;
import com.spring.ocr.rest.restconsume.service.UserService;
import com.spring.ocr.rest.restconsume.util.CustomErrorType;
#RestController
#RequestMapping("/api")
public class RestApiController {
#Autowired
UserService userService;
#RequestMapping(method = RequestMethod.GET, value = "/user/")
public ResponseEntity<List<User>> listAllUsers() {
final List<User> users = userService.findAllUsers();
if(users.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<List<User>>(users, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.GET, value = "/user/{id}")
public ResponseEntity<?> getUser(#PathVariable("id") long id) {
final User user = userService.findUserById(id);
if(user == null) {
return new ResponseEntity<>(new CustomErrorType(String.format("User with id %s not found", id)), HttpStatus.NOT_FOUND);
}
return new ResponseEntity<User>(user, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.POST, value = "/user/")
public ResponseEntity<?> createUser(#RequestBody User user, UriComponentsBuilder ucBuilder) {
if(userService.doesUserExist(user)) {
return new ResponseEntity<>(new CustomErrorType(String.format("Unable to create, user %s already exists", user.getName())), HttpStatus.CONFLICT);
}
userService.createUser(user);
final HttpHeaders headers = new HttpHeaders();
headers.setLocation(ucBuilder.path("/api/user/{id}").buildAndExpand(user.getId()).toUri());
return new ResponseEntity<String>(headers, HttpStatus.CREATED);
}
#RequestMapping(method = RequestMethod.PUT, value = "/user/{id}")
public ResponseEntity<?> updateUser(#PathVariable("id") long id,#RequestBody User user) {
User currentUser = userService.findUserById(id);
if(currentUser == null) {
return new ResponseEntity<>(new CustomErrorType(String.format("Unable to create update, User with id %s not found", user.getId())), HttpStatus.NOT_FOUND);
}
currentUser.setName(user.getName());
currentUser.setAge(user.getAge());
currentUser.setSalary(user.getSalary());
userService.updateUser(currentUser);
return new ResponseEntity<User>(currentUser, HttpStatus.OK);
}
#RequestMapping(method = RequestMethod.DELETE, value = "/user/{id}")
public ResponseEntity<?> deleteUser(#PathVariable("id") long id) {
User user = userService.findUserById(id);
if(user == null) {
return new ResponseEntity<>(new CustomErrorType(String.format("Unable to delete user, user with id %s not found", id)), HttpStatus.NOT_FOUND);
}
userService.deleteUserById(id);
return new ResponseEntity<User>(HttpStatus.NO_CONTENT);
}
#RequestMapping(method = RequestMethod.DELETE, value = "/user/")
public ResponseEntity<User> deleteAllUsers() {
userService.deleteAllUsers();
return new ResponseEntity<User>(HttpStatus.NO_CONTENT);
}
}
And I've set up a test of the web layer using mockMvc, with the user service bean mocked out as is standard:
RestApiControllerUnitTest.java
package com.spring.ocr.rest.restconsume.controller;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.spring.ocr.rest.restconsume.model.User;
import com.spring.ocr.rest.restconsume.service.UserService;
#RunWith(SpringRunner.class)
#WebMvcTest(RestApiController.class)
public class RestApiControllerUnitTest {
#Autowired
private MockMvc mockMvc;
#MockBean
UserService userService;
#Autowired
ObjectMapper objectMapper;
private final List<User> dummyUserList = getDummyUserList();
private final User dummyUser = new User((long)1, "Dave", (short)30, (double)30000);
private final User dummyUpdatedUser = new User((long)1, "David", (short)31, (double)35000);
#Test
public void test_listAllUsers_userListSizeIs4_returnsListSizeOf4AndOkStatus() throws Exception {
when(userService.findAllUsers()).thenReturn(dummyUserList);
this.mockMvc.perform(get("/api/user/"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(4)));
}
#Test
public void test_listAllUsers_userListIsEmpty_returnsNoContentStatus() throws Exception {
when(userService.findAllUsers()).thenReturn(new ArrayList<User>());
this.mockMvc.perform(get("/api/user/"))
.andDo(print())
.andExpect(status().isNoContent());
}
#Test
public void test_getUser_userExists_returnsUser() throws Exception {
when(userService.findUserById(dummyUser.getId())).thenReturn(dummyUser);
this.mockMvc.perform(get("/api/user/" + dummyUser.getId()))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.id",is((int)dummyUser.getId())))
.andExpect(jsonPath("$.name", is(dummyUser.getName())))
.andExpect(jsonPath("$.age", is((int)dummyUser.getAge())))
.andExpect(jsonPath("$.salary", is(dummyUser.getSalary())));
}
#Test
public void test_getUser_userDoesntExist_returnsNotFoundStatusAndCustomErrorString() throws Exception {
when(userService.findUserById(dummyUser.getId())).thenReturn(null);
this.mockMvc.perform(get("/api/user/"+dummyUser.getId()))
.andDo(print())
.andExpect(status().isNotFound())
.andExpect(content().string(containsString("User with id 1 not found")));
}
#Test
public void test_createUser_userDoesNotExist_userCreated() throws Exception {
final String dummyUserJson = objectMapper.writeValueAsString(dummyUser);
when(userService.doesUserExist(dummyUser)).thenReturn(false);
MvcResult result = this.mockMvc.perform(post("/api/user/")
.contentType(MediaType.APPLICATION_JSON)
.content(dummyUserJson))
.andDo(print())
.andExpect(status().isCreated())
.andReturn();
final String header = result.getResponse().getHeader("Location");
assertThat(header, is("http://localhost/api/user/1"));
}
#Test
public void test_createUser_userExists_returnsNotFoundStatusAndCustomErrorMessage() throws Exception {
final String dummyUserJson = objectMapper.writeValueAsString(dummyUser);
final String expectedContent = String.format("Unable to create, user %s already exists", dummyUser.getName());
when(userService.doesUserExist(anyObject())).thenReturn(true);
this.mockMvc.perform(post("/api/user/")
.contentType(MediaType.APPLICATION_JSON)
.content(dummyUserJson))
.andDo(print())
.andExpect(status().isConflict())
.andExpect(jsonPath("$.errorMessage", is(expectedContent)));
}
#Test
public void test_updateUser_userExists_returnsOkStatusAndUpdatedUser() throws Exception {
final String dummyUpdatedUserJson = objectMapper.writeValueAsString(dummyUpdatedUser);
when(userService.findUserById(dummyUser.getId())).thenReturn(dummyUser);
doNothing().when(userService).updateUser(dummyUser);
this.mockMvc.perform(put("api/user/" + dummyUser.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(dummyUpdatedUserJson))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.age", is(dummyUpdatedUser.getAge())))
.andExpect(jsonPath("$.name", is(dummyUpdatedUser.getName())))
.andExpect(jsonPath("$.salary", is(dummyUpdatedUser.getSalary())));
}
#Test
public void test_deleteUser_userExists_returnNoContentStatus() throws Exception {
when(userService.findUserById(dummyUser.getId())).thenReturn(dummyUser);
this.mockMvc.perform(delete("api/user/" + dummyUser.getId()))
.andDo(print())
.andExpect(status().isNotFound());
}
#Test
public void test_deleteUser_userDoesntExist_returnsNotFoundStatusAndCustomErrorMessage () throws Exception {
when(userService.findUserById(dummyUser.getId())).thenReturn(null);
final String expectedContent = String.format("Unable to create update, User with id %s not found", dummyUser.getName());
this.mockMvc.perform(delete("api/user/"+dummyUser.getId()))
.andDo(print())
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.errorMessage", is(expectedContent)));
}
#Test
public void test_deleteAllUsers_usersListPopulated_returnNoContentStatus() throws Exception {
this.mockMvc.perform(delete("api/user/").contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isNotFound());
}
private List<User> getDummyUserList() {
final List<User> dummyUserList = new ArrayList<>();
dummyUserList.add(new User((long)1, "Dave", (short)30, (double)30000));
dummyUserList.add(new User((long)2, "Jess", (short)20, (double)20000));
dummyUserList.add(new User((long)3, "Mike", (short)40, (double)40000));
dummyUserList.add(new User((long)4, "Molly", (short)50, (double)50000));
return dummyUserList;
}
}
The test test_updateUser_userExists_returnsOkStatusAndUpdatedUser is returning a 404 rather than a 200 status and test_deleteUser_userDoesntExist_returnsNotFoundStatusAndCustomErrorMessage is not returning the error message in the body, which alludes to the 404 not being a "genuine" 404(its not returned because the correct response is coming back, its returning it for some other reason). Im also thinking that some of the other 404 statuses may be returned under the same context.
Issue was due to missing "/" on the controller methods used in the failing tests.

Push data to Soap webservice with Spring

Hi I've referred to this link for consuming a SOAP webservice.
But i'm not sure how to call the client method.
Please find my code below :
ClientConfig.java
package com.exclusively.unicommerce.service;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
#Configuration
public class ClientConfig {
#Bean
public Jaxb2Marshaller marshaller()
{
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.unicommerce.wsdl");
return marshaller;
}
#Bean
public SaleOrderClient saleorderclient(Jaxb2Marshaller marshaller) {
SaleOrderClient client = new SaleOrderClient();
client.setDefaultUri("https://link.com/services/soap/?version=1.6");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
SaleOrderClient.java
public class SaleOrderClient extends WebServiceGatewaySupport{
private static final String uri = "https://link.com/services/soap/?version=1.6";
public String createSaleOrder(Suborder suborder)
{
SaleOrder saleorder = new SaleOrder();
saleorder = setSaleOrderObject(suborder);
CreateSaleOrderRequest request = new CreateSaleOrderRequest();
request.setSaleOrder(saleorder);
//PLEASE NOTE THIS Line of CODE.
this.getWebServiceTemplate().marshalSendAndReceive(uri,request);
return "Pushed to Unicommerce";
}
public SaleOrder setSaleOrderObject(Suborder suborder)
{
SaleOrder saleorder = new SaleOrder();
saleorder.setAdditionalInfo(null);
saleorder.setAddresses(null);
saleorder.setCashOnDelivery(null);
saleorder.setCFormProvided(null);
saleorder.setChannel(null);
saleorder.setCode(null);
saleorder.setCurrencyCode(null);
saleorder.setCustomerCode(null);
saleorder.setDisplayOrderCode(null);
saleorder.setNotificationEmail(null);
saleorder.setNotificationMobile(null);
saleorder.setVerificationRequired(null);
return saleorder;
}
}
SuborderController.java
#Controller
public class SuborderController {
private String currentStatus, finalStatus,status,response;
#Autowired
private SuborderService suborderservice;
#RequestMapping(value = "/orders/add", method = RequestMethod.POST)
#ResponseBody
public String addOrders(#RequestBody Suborder order) {
if(order.getSuborderId() == null || order.getSuborderId().isEmpty())
return "BAD REQUEST";
suborderservice.addOrders(order);
//**CALL To createSaleorder(order)**
//response = saleorderclient.createSaleorder(order);
return response;
}
Here things to note is that webservice has provided request class but no response class. Second i tried
#Autowired
SaleOrderClient saleorderclient;
But it threw bean not found exception.
I'm not able to understand how do i access this method.
Please help. TIA.
I resolved my issue by adding below mentioned lines in SuborderController.java
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(ClientConfig.class);
ctx.refresh();
SaleOrderClient saleorderclient = ctx.getBean(SaleOrderClient.class);

Categories