ExceptionHandler is not entered - java

I have a REST-Backend created with JHipster. There are different exception-classes in the service layer and the web-rest layer. This service-exceptions are translated by an ExceptionTranslator which implements the ProblemHandling interface from org.zalando.problem.spring.web.advice
I have the following ExceptionTranslator:
#ControllerAdvice
public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait {
#Override
public ResponseEntity<Problem> process(#Nullable ResponseEntity<Problem> entity, NativeWebRequest request) {
//generated by jHipster
}
#ExceptionHandler(HouseWithoutOwnerServiceException.class)
public ResponseEntity<Problem> handleHouseWithoutOwnerException(HouseWithoutOwnerServiceException ex, NativeWebRequest request) {
return create(new HouseWithoutOwnerException(), request);
}
}
The service-exception class:
public class HouseWithoutOwnerServiceException extends RuntimeException {
public HouseWithoutOwnerServiceException() {
super("House without owner!");
}
}
The rest-error class:
public class HouseWithoutOwnerException extends AbstractThrowableProblem {
private static final long serialVersionUID = 1L;
public HouseWithoutOwnerException() {
super(ErrorConstants.HOUSE_WITHOUT_OWNER_TYPE, "House does not have an owner", Status.CONFLICT);
}
}
In my test the HouseWithoutOwnerServiceException is thrown but not translated into a HouseWithoutOwnerException:
#SpringBootTest(classes = HouseApp.class)
public class HouseControllerIT {
#Autowired
private MappingJackson2HttpMessageConverter jacksonMessageConverter;
#Autowired
private PageableHandlerMethodArgumentResolver pageableArgumentResolver;
#Autowired
private ExceptionTranslator exceptionTranslator;
private MockMvc restHouseMockMvc;
#BeforeEach
public void setup() {
HouseController houseController = new HouseController(houseService);
this.restHouseMockMvc = MockMvcBuilders.standaloneSetup(houseController)
.setCustomArgumentResolvers(pageableArgumentResolver)
.setControllerAdvice(exceptionTranslator)
.setMessageConverters(jacksonMessageConverter)
.build();
}
#Test
#Transactional
public void createHouseWithoutExistingOwner() throws Exception {
HouseDTO houseDTO = createHouseDTOWithoutOwner();
houseDTO.setOwnerId(ownerId + 1); //not existing
restHouseMockMvc.perform(post("/api/v1/houses")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(houseDTO)))
.andExpect(status().isConflict());
}
}
Therefore I always get 500 Internal Server Error instead of 409 Conflict. I debugged it already and the method in the ExceptionTranslator is not entered.

Related

Better solution to call method from interface in every service

I would like to create service which searching and returns objects from repositories, so:
I created interface which has method:
public interface ShapeServicesInterface {
List<ShapeEntity> getAll();
String getName();
}
and few services which implements that interface:
#Service
#RequiredArgsConstructor
public class CircleEntityService implements ShapeServicesInterface {
private final CircleEntityRepository circleEntityRepository;
#Override
public List<ShapeEntity> getAll() {
return new ArrayList<>(circleEntityRepository.findAll());
}
#Override
public String getName() {
return "circle";
}
}
and second one:
#Service
#RequiredArgsConstructor
public class SquareEntityService implements ShapeServicesInterface {
private final SquareEntityRepository squareEntityRepository;
#Override
public List<ShapeEntity> getAll() {
return new ArrayList<>(squareEntityRepository.findAll());
}
#Override
public String getName() {
return "square";
}
}
and next in other service I would like to call that method for getting all entites from that repositories (entites extend abstract class ShapeEntity) - found solution like that:
#Service
#RequiredArgsConstructor
public class TestService {
private final ShapeServiceFacade facade;
private final ExecutorService executorService;
public List<ShapeEntity> getAll() throws ExecutionException, InterruptedException {
List<ShapeEntity> allShapes = new ArrayList<>();
List<Future<List<ShapeEntity>>> futures = new ArrayList<>();
for (ShapeServicesInterface shapeDownloader : facade.getServices()) {
futures.add(executorService.submit(new ShapeTask(shapeDownloader)));
}
for (Future<List<ShapeEntity>> future : futures) {
allShapes.addAll(future.get());
}
return allShapes;
}
ShapeTask is:
#RequiredArgsConstructor
private static class ShapeTask implements Callable<List<ShapeEntity>> {
private final ShapeServicesInterface servicesInterface;
#Override
public List<ShapeEntity> call() {
return servicesInterface.getAll();
}
}
Facade is:
#Service
public class ShapeServiceFacade {
private final Map<String, ShapeServicesInterface> shapeServices;
public ShapeServiceFacade(Set<ShapeServicesInterface> allServices) {
this.shapeServices = allServices.stream()
.collect(Collectors.toMap(ShapeServicesInterface::getName,Function.identity()));
}
public List<ShapeServicesInterface> getServices() {
return new ArrayList<>(shapeServices.values());
}
}
but it is a little complicated. Is there a easier way to call that methods? I would like to add more methods so I will have to implement another task and another method in service, and in interface. I care about searching in every repostiory.
Maybe the ShapeServiceFacade can be omitted, if you are using spring boot, like that
#Service
#RequiredArgsConstructor
public class TestService {
#Autowired
private final List<ShapeServicesInterface> serviceList;
private final ExecutorService executorService;
public List<ShapeEntity> getAll() throws ExecutionException, InterruptedException {
List<ShapeEntity> allShapes = new ArrayList<>();
List<Future<List<ShapeEntity>>> futures = new ArrayList<>();
for (ShapeServicesInterface shapeDownloader : serviceList) {
futures.add(executorService.submit(new ShapeTask(shapeDownloader)));
}
for (Future<List<ShapeEntity>> future : futures) {
allShapes.addAll(future.get());
}
return allShapes;
}

Unit Test could not pass due to constructur error

I am struggling at wrting unit test, when I test my code block, it says : constructor error
my code below;
#Component
public class CodeConfigBuilder {
#Value("${promoConfig.prefix.length}")
private Integer prefixLength;
public void validateRequestAndSetDefaults(PromoRequest promoRequest) {
prefixAndPostFixControlAccordingToLength(promoRequest);
}
private void prefixAndPostFixControlAccordingToLength(PromoRequest promoRequest) {
if (promoRequest.getPostfix() != null) {
int lengthControl = prefixLength + promoRequest.getPostfix().length();
if (lengthControl >= promoRequest.getLength()) {
throw new BadRequestException(Constant.ClientConstants.THE_SUM_OF_PREFIX_AND_POSTFIX_CAN_NOT_BE_GREATER_THAN_LENGHT);
}
}
}
public void validateRequestAndSetDefaults(PromoRequest promoRequest) {
prefixAndPostFixControlAccordingToLength(PromoRequest promoRequest)
}
my yml configuration below;
#========= Promo Config ========== #
promoConfig:
prefix:
length: 3
my service below;
public void validateRequest(PromoRequest promoRequest) {
codeConfigBuilder.validateRequestAndSetDefaults(promoRequest);
}
I have a created PropertySourceResolver class
#Value("${promoGenerationConfig.prefix.length}")
private Integer prefixLength;
and my test class below;
#ExtendWith(SpringExtension.class)
class CodeConfigBuilderTest {
private final PromonRequest promoRequest;
private final PropertySourceResolver propertySourceResolver;
private final PromoService promoService;
private final Request request;
public CodeConfigBuilderTest(PromonRequest promoGenerationRequest, PropertySourceResolver propertySourceResolver, PromoService promoService, Request request) {
this.PromonRequest = PromonRequest ;
this.propertySourceResolver = propertySourceResolver;
this.promoService = promoService;
this.request = request;
}
#Test
void prefixAndPostFixControlAccordingToLength() {
promoService.validateRequest(promoRequest);
int lengthControl = propertySourceResolver.getPrefixLength() + promoRequest.getPostfix().length();
Assertions.assertTrue(true, String.valueOf(lengthControl));
}
I have tried many things but my code does not pass the test it says "org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter"
any help, thank you
I'm not 100% but IMHO you can't use constructor injection in unit tests.
Use this instead:
#SpringBootTest
class CodeConfigBuilderTest {
#Autowired
private PromonRequest promoRequest;
#Autowired
private PropertySourceResolver propertySourceResolver;
#Autowired
private PromoService promoService;
#Autowired
private Request request;

Why my Mono should be subscribed while another case it runs correctly

on this code, orderRepository.save(orders) is not working until i subscribe it. But i have called subscribe from my RestController.
#Service
public class CompleteOrderCommandImpl implements CompleteOrderCommand {
#Autowired
private OrderRepository orderRepository;
#Override
public Mono<Boolean> execute(CancelOrderCommandRequest request) {
return orderRepository.findById(request.getOrderId())
.flatMap(this::changeToCancel)
.map(order -> true);
}
private Mono<Orders> changeToCancel(Orders orders) {
orders.setStatus(StatusEnum.COMPLETED.toString());
return orderRepository.save(orders);
}
}
i have no idea why this happens while another case like this. restaurantRepository.save(restaurant) run without subscribe. because it is (subscribe) called in RestController.
#Service
public class ApprovePendingRestaurantCommandImpl implements ApprovePendingRestaurantCommand {
#Autowired
private RestaurantRepository restaurantRepository;
#Override
public Mono<RestaurantResponse> execute(ApprovePendingRestaurantCommandRequest request) {
return restaurantRepository.findById(request.getRestaurantId())
.flatMap(this::changeRestaurantStatus)
.map(this::constructResponse);
}
private Mono<Restaurant> changeRestaurantStatus(Restaurant restaurant) {
restaurant.setStatus(true);
return restaurantRepository.save(restaurant);
}
private RestaurantResponse constructResponse(Restaurant restaurant) {
RestaurantResponse response = new RestaurantResponse();
BeanUtils.copyProperties(restaurant, response);
return response;
}
}
this is my controller
#PostMapping("/api/merchant/orders/{orderId}/complete")
public Mono<Response<Boolean>> completeOrder(Authentication authentication, #PathVariable String orderId) {
return commandExecutor.execute(CompleteOrderCommand.class, constructCancelOrderCommandRequest(authentication, orderId))
.map(ResponseHelper::ok)
.subscribeOn(Schedulers.elastic());
}
#PostMapping("/api/admin/restaurant/{restaurantId}")
public Mono<Response<RestaurantResponse>> approvePendingRestaurant(#PathVariable String restaurantId) {
return commandExecutor.execute(ApprovePendingRestaurantCommandImpl.class, constructApprovePendingRestaurantCommandRequest(restaurantId))
.map(ResponseHelper::ok)
.subscribeOn(Schedulers.elastic());
}

Spring validator custom HTTP status

I'd like to return a custom HTTP status 422 instead of a default 400 on a spring validation.
My validator:
#Component
#RequiredArgsConstructor
public class EmailUpdateDtoValidator implements Validator {
private Errors errors;
private EmailUpdateDto emailUpdateDto;
#Override
public boolean supports(Class<?> clazz) {
return EmailUpdateDto.class.equals(clazz);
}
#Override
public void validate(Object object, Errors errors) {
this.errors = errors;
this.emailUpdateDto = (EmailUpdateDto) object;
validateEmail();
}
private void validateEmail() {
if (!Email.isValid(emailUpdateDto.getEmail())) {
errors.rejectValue("email", UserValidationErrorCodes.EMAIL_NOT_VALID.name());
}
}
}
How I setup the validation in the Controller:
#Slf4j
#RestController
#RequiredArgsConstructor
public class UserController {
private final EmailUpdateDtoValidator emailUpdateDtoValidator;
#InitBinder("emailUpdateDto")
protected void initEmailValidationBinder(final WebDataBinder binder) {
binder.addValidators(emailUpdateDtoValidator);
}
#RequestMapping(value = "/users/{hashedId}/email", method = RequestMethod.PUT)
public void updateEmail(#RequestBody #Valid EmailUpdateDto emailUpdateDto) {
...
}
}
Using this setup I always get a 400. How could I customize the HTTP status on the return?
Thanks
The validation process would throw an org.springframework.web.bind.MethodArgumentNotValidException, therefore you can add an exception handler to your controller:
import org.springframework.web.bind.MethodArgumentNotValidException;
#ExceptionHandler
public ResponseEntity<String> handleException(MethodArgumentNotValidException ex) {
return new ResponseEntity<String>(HttpStatus.UNPROCESSABLE_ENTITY);
}
As workaround you can define a ExceptionHandler and override the default behavior.
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseEntity<Object> customHttpStatus() {
return ResponseEntity.status(422).build();
}
}

ExceptionMapper toResponse not Invoking while running junit test case. but is invoking when running through Client/Postman

I have a test case where am throwing exception incase of some basic validation. but ExceptionMapper is not being invoked. But if i run from postman to hit the service it is working fine.
Do Junit test have to run differently for ExceptionMapper ?
Test case :
#Test
public void itShouldHavePersonNumber() {
RestAuthController controller = new RestAuthController();
Response response = controller.insertGuid(null, "m012");
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> {controller.insertGuid(null, "m012");});
assertThat(response.getStatus()).isEqualTo(Status.BAD_REQUEST.getStatusCode());
}
Controller:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response insertGuid(#QueryParam("personNumber") Integer personNumber, #QueryParam("guId") String guId ) throws ValidationException {
if(guId == null || guId.isEmpty()) {
throw new ValidationException("guId is Required");
}
}
Exception Mapper :
#Provider
public class ValidationMapper implements ExceptionMapper<ValidationException> {
#Override
public Response toResponse(ValidationException ex) {
return Response.status(Response.Status.BAD_REQUEST).entity(ex.getMessage()).type(MediaType.TEXT_PLAIN).build();
}
}
Exception:
public class ValidationException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public ValidationException() {
super();
}
public ValidationException(String message, Throwable cause) {
super(message, cause);
}
public ValidationException(String message) {
super(message);
}
}
Why do you think the exception mapper should be called? It is not an integration test. All you are doing is instantiating the class and then calling a method. There is nothing magical in Java that will make the exception mapper be called. You need to run an integration test with the Jersey application running (and the mapper registered) if you want the mapper to be called.
One way to run an integration test with Jersey is to use it's Test Framework. Below is an example.
public class ValidationExceptionTest extends JerseyTest {
public static class ValidationException extends RuntimeException {}
public static class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {
#Override
public Response toResponse(ValidationException e) {
return Response.status(400).entity("boo boo").build();
}
}
#Path("echo-name")
public static class EchoNameResource {
#GET
public String echoName(#QueryParam("name") String name) {
if (name == null || name.isEmpty()) {
throw new ValidationException();
}
return name;
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(EchoNameResource.class)
.register(ValidationExceptionMapper.class);
}
#Test
public void testResponseOkWithQueryParam() {
final Response response = target("echo-name")
.queryParam("name", "peeskillet")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.readEntity(String.class)).isEqualTo("peeskillet");
}
#Test
public void testResponseBadRequestWithNoQueryParam() {
final Response response = target("echo-name")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(400);
}
}

Categories