Reactive programming - With default and exception handling - reactive-programming

I am trying to find the best approach for default and error handling with reactive.
This is a working code.
public Mono<ServerResponse> retrieveById(ServerRequest request) {
final String id = request.pathVariable("ID");
return context.retrieveUser().flatMap(usr -> {
return repository.findByApptId(Long.parseLong(id), usr.getOrgId()).flatMap(appt -> {
return ServerResponse.ok().contentType(APPLICATION_JSON).bodyValue(appt);
});
});
}
I am trying to add default and error handling.
For default,
return context.retrieveUser().flatMap(usr -> {
return repository.findByApptId(Long.parseLong(apptId), usr.getOrgId()).flatMap(appt -> {
return ServerResponse.ok().contentType(APPLICATION_JSON).bodyValue(appt);
}).defaultIfEmpty(ServerResponse.notFound().build());
The above default add gives error.
The method defaultIfEmpty(ServerResponse) in the type
Mono is not applicable for the arguments
(Mono)
Same error with onErrorReturn as well.

Mono#defaultIfEmpty takes an item T and returns a Mono<T>. Which means that if you give it a String it will return a Mono<String>.
You are giving it ServerResponse.notFound().build() which returns a Mono<ServerResponse> which won’t work since it would give Mono<Mono<ServerResponse>>.
What you are looking for is Mono#switchIfEmpty which switches producer (Mono) to another producer if the previous one turned out empty.

Related

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<CommandAPI.Dtos.CommandReadDto>' to 'Microsoft.AspNetCore.Mvc.ActionResult

Every time I test for the target action: GetAllCommands(), I face this error!
Here my code:
[HttpGet]
public ActionResult<IEnumerable<CommandReadDto>> GetAllCommands()
{
var commandItems = _repository.GetAllCommands();
return _mapper.Map<IEnumerable<CommandReadDto>>(commandItems);//error-pane code
}
But when I return the result set as follow, it works correctly:
return Ok(_mapper.Map<IEnumerable<CommandReadDto>>(commandItems));
could anyone explain it to me?
This is because the ActionResult types represent various HTTP status codes.
You have an ActionResult<IEnumerable<CommandReadDto>> endpoint return type, but you are trying to return an IEnumerable<CommandReadDto> with no defined code status, so it needs to be wrapped with Ok() or NotFound() methods, etc.
If you want to return IEnumerable<CommandReadDto>, then remove ActionResult from the return type and leave only IEnumerable<CommandReadDto> there, then it will work and automatically return status code 200

How to chain reactive operations after empty Mono without blocking?

Basically what I am trying to achieve is to call a second repository (a ReactiveCrudRepository) or throw an exception, depending on the result of a call to a first repository.
My original idea looks like this:
/** Reactive with blocking code */
public Flux<SecondThing> getThings(String firstThingName) {
FirstThing firstThing = firstRepo
.findByName(firstThingName)
// Warning: "Inappropriate blocking method call"
.blockOptional() // this fails in test-context
.orElseThrow(() -> new FirstThingNotFound(firstThingName));
return secondRepo.findAllByFirstThingId(firstThing.getId());
}
Which would correspond to the following non-reactive approach:
/** Non-reactive */
public List<SecondThing> getThings(String firstThingName) {
FirstThing firstThing = firstRepo
.findByName(firstThingName)
.orElseThrow(() -> new FirstThingNotFound(firstThingName));
return secondRepo.findAllByFirstThingId(firstThing.getId());
}
I haven't found a way to do this in a reactive non-blocking way. All I need is to throw an error if an empty Mono comes out of the first call, and continue the pipeline if not empty; but I could not seem to use onErrorStop or doOnError correctly here, and map does not help as it skips the empty Mono.
What I have is a workaround if I use id instead of name, but I'm not quite satisfied with it as it shows a different behaviour in the case where is an instance of FirstThing but no SecondThing linked to it:
/** Reactive workaround 1 */
public Flux<SecondThing> getThings(Long firstThingId) {
return secondRepo
.findAllByFirstThingId(firstThingId)
.switchIfEmpty(
Flux.error(() -> new FirstThingNotFound(firstThingName))
);
}
Another workaround I've found is the following, that replaces the empty Mono with a null value, but it doesn't look right and throws a warning too:
/** Reactive workaround 2 */
public Flux<SecondThing> getThings(String firstThingName) {
return firstRepo
.findByName(firstThingName)
// Warning: "Passing 'null' argument to parameter annotated as #NotNull"
.defaultIfEmpty(null)
.flatMapMany(
firstThing -> secondRepo.findAllByFirstThingId(firstThing.getId()
)
.onErrorMap(
NullPointerException.class, e -> new FirstThingNotFound(firstThingName)
);
}
What is the correct way to chain the calls to both repositories so that the presence or absence of a FirstThing with the requested firstThingName conditions the call to the second repo?
I found a solution so simple, that I could be ashamed not to have found it earlier:
public Flux<SecondThing> getThings(String firstThingName) {
return firstRepo
.findByName(firstThingName)
.switchIfEmpty(Mono.error(() -> new FirstThingNotFound(firstThingName)))
.flatMapMany(
firstThing -> secondRepo.findAllByFirstThingId(firstThing.getId()
);
}
The trick is that switchIfEmpty does not force you to pick a "valid" replacement value, so it is possible to use a Mono.error to propagate the right exception directly.

What is the switchIfEmpty alternative in Smallrye Mutiny

In RxJava 2 and Reactor there is a switchIfEmptylike method to switch to new flow if there is no elements in current flow.
But when I began to use Minuty, I can not find an alternative when I convert my Quarkus sample to use the Reactive features.
Currently my solution is: in my PostRepository, I use an exception to indicate there is no post found.
public Uni<Post> findById(UUID id) {
return this.client
.preparedQuery("SELECT * FROM posts WHERE id=$1", Tuple.of(id))
.map(RowSet::iterator)
.flatMap(it -> it.hasNext() ? Uni.createFrom().item(rowToPost(it.next())) : Uni.createFrom().failure(()-> new PostNotFoundException()));
}
And catch it in the PostResource.
#Path("{id}")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Uni<Response> getPostById(#PathParam("id") final String id) {
return this.posts.findById(UUID.fromString(id))
.map(data -> ok(data).build())
.onFailure(PostNotFoundException.class).recoverWithItem(status(Status.NOT_FOUND).build());
}
How to return an Uni means 0 or 1 element in PostRepository, and use a switchIfEmpty like method in PostResource to build the alternative path for the flow?
Uni cannot be empty in the sense it always contains an item (potentially null).
So, the equivalent of switchIfEmpty is uni.onItem().ifNull().switchTo(() -> ...)

Using project reactor mergeWith() operator in order to achieve "if/elseif/else" branching logic

I am trying to use project reactor mergeWith operator in order to achieve a if/elseif/else branching logic as described here: RxJS, where is the If-Else Operator.
The provided samples are written in RxJS but the underlying idea remains the same.
Basically the idea is to use the filter operator on 3 monos/publishers (therefore with 3 different predicates) and merge the 3 monos as follows (here they are RxJS Observables of course):
const somethings$ = source$
.filter(isSomething)
.do(something);
const betterThings$ = source$
.filter(isBetterThings)
.do(betterThings);
const defaultThings$ = source$
.filter((val) => !isSomething(val) && !isBetterThings(val))
.do(defaultThing);
// merge them together
const onlyTheRightThings$ = somethings$
.merge(
betterThings$,
defaultThings$,
)
.do(correctThings);
I have copied and pasted the relevant sample from the above article.
Consider that something$, betterThings$ and defaultThings$ are our monos isSomething & isBetterThings are the predicates.
Now here are my 3 real monos/publishers (written in java):
private Mono<ServerResponse> validateUser(User user) {
return Mono.just(new BeanPropertyBindingResult(user, User.class.getName()))
.doOnNext(err -> userValidator.validate(user, err))
.filter(AbstractBindingResult::hasErrors)
.flatMap(err ->
status(BAD_REQUEST)
.contentType(APPLICATION_JSON)
.body(BodyInserters.fromObject(err.getAllErrors()))
);
}
private Mono<ServerResponse> validateEmailNotExists(User user) {
return userRepository.findByEmail(user.getEmail())
.flatMap(existingUser ->
status(BAD_REQUEST)
.contentType(APPLICATION_JSON)
.body(BodyInserters.fromObject("User already exists."))
);
}
private Mono<ServerResponse> saveUser(User user) {
return userRepository.save(user)
.flatMap(newUser -> status(CREATED)
.contentType(APPLICATION_JSON)
.body(BodyInserters.fromObject(newUser))
);
}
Here is the top level method that needs to merge the three publishers:
public Mono<ServerResponse> signUpUser(ServerRequest serverRequest) {
return serverRequest.bodyToMono(User.class)
.mergeWith(...)
}
I am not sure how to use the mergeWith() operator... I have tried the Mono.when() static operator which takes several publishers (good for me) but returns a Mono<void> (bad for me).
Can anyone please help?
P.S. I am sure you will excuse the mix between RxJS (js) and Reactor code (java). I meant to use my knowledge from RxJS in order to achieve a similar goal in my Reactor app. :-)
edit 1: I have tried this:
public Mono<ServerResponse> signUpUser(ServerRequest serverRequest) {
return serverRequest
.bodyToMono(User.class)
.flatMap(user -> validateUser(user).or(validateEmailNotExists(user)).or(saveUser(user))).single();
}
But I get this error: NoSuchElementException: Source was empty
edit 2: Same with (notice the parenthesis):
public Mono<ServerResponse> signUpUser(ServerRequest serverRequest) {
return serverRequest
.bodyToMono(User.class)
.flatMap(user -> validateUser(user).or(validateEmailNotExists(user)).or(saveUser(user)).single());
}
edit 3: Same error with a Mono<User>:
public Mono<ServerResponse> signUpUser(ServerRequest serverRequest) {
Mono<User> userMono = serverRequest.bodyToMono(User.class);
return validateUser(userMono)
.or(validateEmailNotExists(userMono))
.or(saveUser(userMono))
.single();
}
edit 4: I can confirm that at least one of the three monos will always emit. It is when I use the or() operator that something goes wrong...
If I use this, all my tests pass:
public Mono<ServerResponse> signUpUser(ServerRequest serverRequest) {
return serverRequest.bodyToMono(User.class)
.flatMap(user -> Flux.concat(validateUser(user), validateEmailNotExists(user), saveUser(user)).next().single());
}
I have used the concat() operator here to preserve the order of operations.
Do you know what I am getting wrong with the or() operator?
edit 5: I have tried with the cache() operator as follows to no avail:
public Mono<ServerResponse> signUpUser(ServerRequest serverRequest) {
return serverRequest
.bodyToMono(User.class)
.cache()
.flatMap(user -> validateUser(user)
.or(validateEmailNotExists(user))
.or(saveUser(user))
.single()
);
}
Your current code sample implies that your 3 methods returning Mono<ServerResponse> should be taking a Mono<User> rather than a User, so you may need to alter something there.
However, I digress - that doesn't seem to be the main question here.
From what I understand of the pattern described in that link, you're creating 3 separate Mono objects, only one of which will ever return a result - and you need a Mono of whichever one of your original 3 Mono objects returns.
In that case, I'd recommend something like the following:
Mono<ServerResult> result = Flux.merge(validateUser(user), validateEmailNotExists(user), saveUser(user)).next().single();
Breaking it down:
The static Flux.merge() method takes your 3 Mono objects and merges them into a Flux;
next() returns the first available result as a Mono;
single() will ensure that the Mono emits a value, as oppose to nothing at all, and throw an exception otherwise. (Optional, but just a bit of a safety net.)
You could also just chain Mono.or() like so:
Mono<ServerResult> result = validateUser(user).or(validateEmailNotExists(user)).or(saveUser(user)).single();
The advantages to this approach are:
It's arguably more readable in some cases;
If it's possible that you'll have more than one Mono in your chain return a result, this allows you to set an order of precedence for which one is chosen (as oppose to the above example where you'll just get whatever Mono emitted a value first.)
The disadvantage is potentially one of performance. If saveUser() returns a value first in the above code, then you still have to wait for the other two Mono objects to complete before your combined Mono will complete.

how to read properties from GWT host page?

What's the best way to read properties from the GWT host page?
I'm trying to find a simple way to embed properties into the host page and access them from Java.
It seems like Dictionary is the recommended way to go, but it's somewhat fragile in that your dictionary must exist in the host page, or it errors with MissingResourceException. Similarly, Dictionary.get() will throw a resource error if the key you request does not exist.
I'm writing a library, and I'd prefer a more robust solution that won't error out if the user doesn't specify the dictionary or key. Should I just catch exceptions thrown by the Dictionary and return null? Is there a more robust way to do this?
EDIT: at the moment, I'm using the following:
protected Dictionary dict;
public ClientConfiguration(String configName)
{
try
{
dict = Dictionary.getDictionary(configName);
} catch (MissingResourceException e)
{
dict = null;
}
}
public String getString(String key)
{
return dict == null ? null : dict.keySet().contains(key) ? dict.get(key) : null;
}
I would say, your solution is very robust already. It's absolutely ok to catch - without rethrowing -
a specific Exception (MissingResourceException)
which you expect to occur (the resource could be missing),
and which will only occur under the circumstances you expect (it would be bad to catch a NullPointerException, because there can be many reasons for that Exception).
You could even change your getString() method a little bit:
public String getString(String key) {
try {
return dict == null ? null : dict.get(key);
} catch (MissingResourceException e) {
return null;
}
}
This should make the method almost twice as fast, because you save the extra call to dict.keySet().contains(key), which takes approximately as long as dict.get(key).