IllegalStateException when fetching by name from database - spring-boot

I'm getting an IllegalStateException when I try to access an entity in my database by a deliberately incorrect name.
I understand why this is happening (obviously) but not sure how I can check to ensure the entity I'm looking for exists in the database. This is what I have at the moment:
override fun getAssetStatus(id: Long): AssetStatus {
log.info("Getting Asset Status $id from database.")
val assetStatus = assetStatusRepository.findById(id).get()
log.info("Asset Status $id is ${assetStatus.name}")
return assetStatus
}
Which, so long as the name is correct, it returns a correct result which does not instantly halt the application.
However, if I then incorrectly spell the name, it provides an IllegalStateException as the entity does not exist.
If I add a null check, it states that assetStatus is always false.
OK... So looking through the JPA docs I should be able to use the following right?
findAssetStatusByNameIsNotNull(name: String): Boolean // Boolean is an assumption
However, this just straight up throws an unable to create bean error.
So, how can I check if the entity exists and if not to continue as normal? I know I could probably put a catch in to get the IllegalStateException but I'd rather halt it with a meaningful message than just the exception above.
This is more of a protective measure on my back end than anything else.

Related

Setting up data for Mockito

I am unit testing my spring Rest API and I have had joy mocking my create and retrieve methods using the following structure.
#Test
public void getAllUsersReturnsAListOfAllTheUsersInJSONFormat() throws Exception {
List<User> users = new ArrayList<>();
users.add(new User("DelBoy"));
users.add(new User("Rodney_Trotter"));
users.add(new User("Uncle.Albert"));
Mockito.when(service.getAllUsers()).thenReturn(users);
mvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.hasSize(3)))
.andExpect(jsonPath("$[0].username", Matchers.equalTo("DelBoy")))
.andExpect(jsonPath("$[1].username", Matchers.equalTo("Rodney_Trotter")))
.andExpect(jsonPath("$[2].username", Matchers.equalTo("Uncle.Albert")));
}
However, I am trying to create a test for my Update and Delete methods. When I mock the methods they never behave as expected because there is no data to update or delete. After extensive research online I cannot find any information pointing to resolving this issue. My current attempt is the following code.
#Test
public void updateUserUpdatesAUserUponSuccess() throws Exception {
User updatedUser = new User(UUID.fromString("da3f6cae-9126-407b-a1ea-093bdac72434"), "Trigger");
Mockito.when(service.updateUser(updatedUser)).thenReturn(true);
Mockito.when(validator.validateUsername(updatedUser.getUsername())).thenReturn(true);
Mockito.when(validator.validateUUID(updatedUser.getUserId().toString())).thenReturn(true);
String str = mvc.perform(put("/api/users/da3f6cae-9126-407b-a1ea-093bdac72434/TriggersBroom"))
.andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
System.out.println(str);
}
I am not sure how to have user data mocked so that when I make a PUT request it can update the user at the hex Id specified to a different username ("trigger" being the username). How I would like this to work is to have a small set of user objects that I can update when I mock my updateUser function so I can test the update user route actually updates a record. Currently it returns false because nothing is being updated but it should return true as I have tested manually in my development environment outside of unit testing and it works.
Note:
The println was my was of checking what was actually returned and it was false.
How I would like this to work is to have a small set of user objects that I can update when I mock my updateUser function so I can test the update user route actually updates a record
This is because you write:
User updatedUser = new User(UUID.fromString("da3f6cae-9126-407b-a1ea-093bdac72434"), "Trigger");
Mockito.when(service.updateUser(updatedUser)).thenReturn(true);
Then you can see the updatedUser is not the same instance as the one you gonna call in the real service, it's just a instance you created above.
To mock this, you have 2 ways:
If you can get the exactly same instance as the one you gonna call in real service, it will return true for you.
You can use Mockito.eq(updatedUser) to mock. E.g. Mockito.when(service.updateUser(Mockito.eq(updatedUser))).thenReturn(true);
But then you need to remember to override the equals() method for class User. Then if you modify the method equals(), you need to update hashCode() as well

Spring reactor doOnNext, is it getting executed?

I was playing around with reactor
public #NotNull Mono<ServerResponse> findXXXXSse(final ServerRequest request) {
return request.bodyToMono(XXXXSearch.class)
.doOnNext(this::validate)
.flatMap(this::findXXXXSse)
.switchIfEmpty(this.emptyBodyException());
}
And I was wondering if the use of .doOnNext(this::validate) was correct or not. From my point of view, I'm not sure the validate is called before the findXXXXSse?
Am I wrong?
Flux.doOnNext method should always be used as a side-effect.
The documentation says "Add behavior (side-effect) triggered when the Flux emits an item." Refer : https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html#doOnNext-java.util.function.Consumer-
I assume you want to resume your chain only when the validation is successful, i.e., .flatMap(this::findXXXXSse) should only be called if validation succeeds.
You can use filter(this::validate) and add a validate(XXXXSearch.class) method to return true/false.

How to handle Optional from Service to Controller in Spring?

Spring JPA returns an Optional. I return the Optional from the Service. There if Optional is not present I pass error to model. The other case if there is a database error for example database not available, I do not catch these exceptions. If that happens user will see this exception in browser. I do not know how to handle this very rare error. For me this should never happen and if it does, ok . I do not want to handle this exception all the time. What do you think about my architecture.
Service:
#Override
public Optional<Client> findClientById(Long id) {
return clientRepository.findById(id);
}
Controller:
Optional<Client> client= clientService.findClientById(id);
if(client.isPresent())
{
model.addAttribute("client", client.get());
}
else
{
model.addAttribute("error", "No clientfound with this ID!!");
}
Firstly, you shouldn't pass error as an attribute in your model - that's REST anti-pattern. The best way to hand this is to use HTTP codes, e.g. return 502. To do so, you may wrap your exceptions up your code into HttpResponse. To catch your exception, you may approach similar to method that explained here, i.e. catch spring data exception, wrap it up a way you wanted and throw on higher level for processing.

Spring mvc controller null return handler

#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public List<Country> getListOfCountries() {
return countryService.listAll();
}
It displays a json view of the object but if the service return null, then I want to display an error message, Any suggestions pls?
First of all, even if this does not directly answer the question, your objects should never ever return null instead of empty collections - you can find the reasoning in Effective Java 2nd Edition, Item 43 / p.201
So, if the situation when no countries were found is normal it must be processed by the client JS code that will check the count and display the respective message.
If something has gone wrong you can throw an exception(as Biju has pointed out +1) - I believe that it's the service who should throw the exception because it knows the reason why it happened, and not to return null anyway.
I'd like to add that in Spring 3.2(in pre Spring 3.2 returning response body is complicated) you can set an #ExceptionHandler that will both return JSON and set the HTTP status code which can be later processed by the client. I think that returning a custom JSON response with some error code is most optimal here.
#RequestMapping("/test")
#ResponseBody
public List<Country> getListOfCountries() {
//assuming that your service throws new NoCountriesFoundException();
//when something goes wrong
return countryService.listAll();
}
#ExceptionHandler(NoCountriesFoundException.class)
ResponseEntity<String> test() {
return new ResponseEntity<String>(
"We are sorry, our server does not know any countries yet.",
HttpStatus.I_AM_A_TEAPOT );
}
Then in the JS code, you can do specific processing depending on the returned status code.
Also, to avoid declaration of the same #ExceptionHandler in different controllers, in Spring 3.2 you can put #ExceptionHandler inside a #ControllerAdvice annotated class.
For details, see http://static.springsource.org/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-exceptionhandlers and http://www.springsource.org/node/3738 for 3.2 specific things
You have a couple of options I think:
If you return a null back, it will be returned as an empty string "", you can probably look for that and handle it.
Return a wrapper type on top of your list, this way if the wrapped list is null something like this will be returned back to the client {"countries":null} which can be more easily handled at the javascript end.
Throw an exception, which will propagate as a 500 status code back to the client, you can then have an error handler on the javascript side to handle this scenario.

Struts2 - disable conversion error

I have a ssn field (represented as a String in Action class) in which the user enters something in the following format 123-23-2233. Struts2 throws an error. I dont know where it is configured for it to throw this as an error. How do I stop this?
I have my own validation in the validate() method, something like this
if(StringUtility.isBlank(person.getSsn()) || !validateRegex(SSN_REGEX,person.getSsn().trim())){
this.addFieldError("person.ssn","SSN is required");
}
The conversion errors will be added to the field errors map before your validate method even runs. As such there is a very simple way of removing them once you get to your validate method. Simply remove the error from the map before adding your own.
Example code below;
if(yourCondition){
// Check whether this field has existing errors and remove them.
List<String> existingErrors = getFieldErrors().get("person.ssn");
if(existingErrors != null){
existingErrors.clear();
}
// Add your own error.
addFieldError("person.ssn","SSN is required");
}
Similarly you could clear the entire field errors map if you wanted to clear the default error messages on all fields.
I hope this helps.

Resources