MockMvc : handling unique constraints - spring-boot

In my project I have a unique constraint on a name value which I annotated like so:
#Column(unique = true)
protected String name;
this is fine, and works...what I want to do in the project deployment, is when a user tries to enter an object in the database with a name that already exists, they'll be presented with an error message "an entry with that name already exists" or something similar and then be redirected back to their form.
In testing with MockMvc, I have something like this:
try {
mockMvc.perform(post(uri)
.contentType(MediaType.APPLICATION_JSON)
.content(widgetJson)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isNotFound());
} catch (Exception e) {
logger.error("we got expected exception here", e);
threwException = true;
e.printStackTrace();
}
after that block, if the threwException var is false, the test fails. Normally, I do not have try/catch in my MockMvc unit tests, rather a throws Exception in the signature.
The way I have it now works, and gives me the results I expect, but I don't get a response. I tried adding a MvcResult result = before the perform call, and adding the .andReturn() at the end, but I never get an actual response and can't determine what the response code is.
I figure this has to be a pretty common use case and was wondering if there is a best practice as to how to set up the test.
thanks!

This might help to solve your problem:
MvcResult result = null;
try {
ResultActions resultActions = mockMvc.perform(post(uri)
.contentType(MediaType.APPLICATION_JSON)
.content(widgetJson)
.accept(MediaType.APPLICATION_JSON));
result = resultActions.andReturn();
resultActions
.andDo(print())
.andExpect(status().isNotFound());
} catch (Exception e) {
if(result != null) {
logger.error("Response Body", result.getResponse().getContentAsString());
logger.error("Response Status", result.getResponse().getStatus() ());
}
logger.error("we got expected exception here", e);
threwException = true;
e.printStackTrace();
}
Reference:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/MvcResult.html#getResponse--
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/mock/web/MockHttpServletResponse.html#getContentAsString--
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/ResultActions.html#andReturn--

Related

RestClientException doesnot Rollback entire transaction

Please refer to the below snippet of code
#Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = { Throwable.class })
#Override
public MyClass myMethod(myParams) {
try{
myRepo.saveAll(myEntityList);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(myUrl,requestParam,String.class);
if(responseEnity.getBody()!=null && StringUtils.equalsIgnoreCase(responseEnity.getBody(),"Failure")){
log.error("Rest Call Failed");
throw new RestClientException("Error in remote service");
}
}catch(Exception e){
e.printStackTrace();
}
}
Now in the console log, I can see RestClientException being thrown but myRepo.saveAll(myEntityList); doesn't rollback instead it is inserting data into DB.
Can anyone figure out the issue?
My requirement is that in RestServiceCall if I get a failure response then my current transaction should get rollback entirely.
Your transaction is not rollbacked because there is no exception thrown, you're catching it.
As #Sumit Ghost mentioned, you can log it and rethrow the exception

java.lang.IllegalStateException: getWriter() has already been called for this response, Even thought its called only once

I am using spring-boot. I want to send a CSV as the attachment of response for which I am using opencsv to write bean to response. Even though response.getWriter() is called only once, I am getting this exception.
While searching for solution, I came to know that we cannot use response.getWriter() and response.getOutputStream() together. But this is not the case here.
I am only calling getWriter and exactly once.
I also checked if it was due to opencsv library by writing a plain string to it e.g. "name, test". But still the same error. So it's not due to opencsv either.
private Pair<Boolean, String> writeCSVToResponse(List<QuestionDownloadResponse> qdrList, HttpServletResponse response) {
String fileName = new SimpleDateFormat("'CSV'yyyyMMddHHmmss'.csv'").format(new Date());
response.reset();
response.setContentType("application/csv");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + fileName + "\"");
response.setCharacterEncoding("UTF-8");
//Find answer, for now we will return any if exists
try {
Writer writer = response.getWriter();
// Create Mapping Strategy to arrange the
// column name in order
final CustomCSVMappingStrategy<QuestionDownloadResponse> mappingStrategy = new CustomCSVMappingStrategy<>();
mappingStrategy.setType(QuestionDownloadResponse.class);
StatefulBeanToCsv<QuestionDownloadResponse> sbc = new StatefulBeanToCsvBuilder<QuestionDownloadResponse>(writer)
.withSeparator(CSVWriter.DEFAULT_SEPARATOR)
.withMappingStrategy(mappingStrategy)
.build();
sbc.write(qdrList);
} catch (IOException e) {
CMSQuestionServiceImpl.logger.error("Error in CSV IO operations", e);
return Pair.of(false, "Failed to Open file");
} catch (CsvDataTypeMismatchException | CsvRequiredFieldEmptyException e) {
CMSQuestionServiceImpl.logger.error("Error in CSV Write operation", e);
return Pair.of(false,"Failed to write in csv");
}
}
Why is this exception coming, even when response.getWriter is called exactly once. How to solve this issue.
I found the issue. The problem was that I was sending a String (a message) in response in the controller, but in service, I am sending an attachment. So getWriter() was being called first time in service by me, and the second time by spring to write the response message of type String. Solution: I set the return type of method in the controller to void ex::
#GetMapping(value = "/downloadCSV")
public void downloadCSV(#RequestBody(required = false) List<Integer> items, HttpServletResponse response){
The only problem is if something goes wrong in service, you cannot send an error message and there are other alternatives to overcome it.
I had the same error: in spring-boot
This error occures when we use a RestExceptionHandler which tries to modify the response by using response.getOutputStream(), remember, the response.getWriter() is already called (even once in our code).
So when we call response.getWriter() , and we have some exception, the RestExceptionHandler executes the response.getOutputStream() which causes java.lang.IllegalStateException: getWriter() has already been called for this response.
What I suggest to do is to add try-catch to the response.getWriter() and when you catch any exception we have to reset the response by using response.reset() like that:
try {
writer.write(beans);
} catch (Exception e) {
response.reset();
throw e;
}
After throwing the exception the RestExceptionHandler will do it's job.

Retrotif2 + RxJava sending POST request failed

I want to send POST request with Retrofit + RxJava, but it is failing and I don't know the reason. In one activity it's working, in another - don't want to work:
private void sendMerchantInfo() {
try {
String advertiserOriginalDeepLink = "https://mywebsite.com/main-1?param1=value1&param2=value2";
String urlGetParams = LinkParser.getUrlGETParams(advertiserOriginalDeepLink);
Map<Object, Object> merchantInfo = LinkParser.parseUrlGetParams(urlGetParams);
String merchantInfoJson = new Gson().toJson(merchantInfo); //{"param1":"value1","param2":"value2"}
String url = "https://api.endpoint.com/v1/system/merchant/process";
userService = this.serviceGenerator.createService(UserService.class, true);
final Observable observable = userService.sendUserInfo(
url, new RetrofitMapBody(merchantInfo))
.doOnNext(new Consumer<ResponseBody>() {
#Override
public void accept(ResponseBody responseBody) throws Exception {
//handle 200 OK.
}
})
.onErrorResumeNext((ObservableSource<? extends ResponseBody>) v ->
Crashlytics.log("Send user info attempt failed."))
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
addDisposable(observable.subscribe());
}
} catch (Exception exception) {
Crashlytics.log("Send user info attempt failed. " + exception.getMessage());
}
}
I suspect that problem in this part, I am trying to send request in OnCreate() method:
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
Tried to use this, but no effect:
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
What I am doing wrong? It always call onErrorResumeNext() It's probably something with threads because one time I got exception: networkonmainthreadexception. Please help.
Try using RxJava2 Adapter, it will save you a lot!
Step 1: Retrofit client setup
private Retrofit getRetrofitClient() {
return new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //option 1
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.newThread())) //option 2
.build();
}
Step 2: APIService interface (Example)
#GET("endpoint")
Single<ResponseModel> fetch();
Step 3: Usage
Single<ResponseModel> fetch() {
return getRetrofitClient()
.create(APIService.class)
.fetch();
}
Any non-2xx HTTP response will be wrapped in HttpException from which you can extract the status code, the status message and the full HTTP response.
Any connection errors will be wrapped in IOException
And that is all you need to do to wrap your network call in any RxJava stream.

spring boot HttpServletResponse not setting file name

I have a rest service like this:
import org.apache.tomcat.util.http.fileupload.IOUtils;
#RequestMapping(value = "/xxx", method = GET)
public void getExcel(HttpServletResponse resp) {
resp.setHeader("Content-Disposition", "attachment; filename=\"NAME.xlsx\"");
resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
try (ServletOutputStream outputStream = resp.getOutputStream()) {
IOUtils.copy(A-VALID-FILE-INPUT-STREAM, outputStream);
resp.flushBuffer();
} catch (IOException e) {
throw new AppException(e);
}
}
the problem is that every time I call this service the default save name is 'response', I have tried returning HttpEntity<byte[]>, create objects like HttpHeaders() but nothing changes.
Any help is appreciated
If you are using postman take a look at https://github.com/postmanlabs/postman-app-support/issues/2082
Seems that you will need to wait until this issue will be addressed by postman team.

Spring Exception Handler log all exceptions but return original response

So I am trying to log all uncaught exceptions returned by the controllers of a spring project in a generic fashion.
I was able to do this with the following exception handler:
#ControllerAdvice
public class ControllerConfig {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public static final String DEFAULT_ERROR_VIEW = "error";
#ExceptionHandler(HttpMessageNotReadableException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public void handleBadRequest(HttpMessageNotReadableException e) {
logger.warn("Returning HTTP 400 Bad Request", e);
throw e;
}
#ExceptionHandler(AccessDeniedException.class)
public void defaultErrorHandler(HttpServletRequest request, Exception e) throws Exception {
logger.error("Error in request:" + request.getRequestURL(), e);
throw e;
}
This also returns the error responses of the request, so I don't have to differentiate between all the different error response codes.
However, for every invocation of the method a second error log is created because of the exception thrown in the method:
Code is from org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#doResolveHandlerMethodException
try {
if (logger.isDebugEnabled()) {
logger.debug("Invoking #ExceptionHandler method: " + exceptionHandlerMethod);
}
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);
}
catch (Exception invocationEx) {
if (logger.isErrorEnabled()) {
logger.error("Failed to invoke #ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
}
return null;
}
So is there a smarter way to return the original exception of the method?
It depends on what do you mean by "a smarter way to return the original exception". What exactly would you like to return to the client? If this is just the message of the exception you can simply return it from the exception handler and annotate the method with #ResponseBody. Spring will do the rest for you.
#ExceptionHandler(HttpMessageNotReadableException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public String handleBadRequest(HttpMessageNotReadableException e) {
logger.warn("Returning HTTP 400 Bad Request", e);
throw e.getMessage();
}
You can also return some custom object which wraps the exception information and any other data that you desire.

Resources