How to correctly chain Mono/Flux calls - spring

I'm having trouble with understanding how to achieve my goal with reactive approach.
Let's assume that I have a Controller, that will return Flux:
#PostMapping(value = "/mutation/stream/{domainId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Mutation> getMutationReactive(#RequestBody List<MutationRequest> mutationRequests, #PathVariable Integer domainId) {
return mutationService.getMutations(mutationRequests, domainId);
}
In service, currently with .subscribeOn(Schedulers.boundedElastic()), because it calls for a blocking code that is wrapped into a Callable.
public Flux<Mutation> getMutations(List<MutationRequest> mutationRequests, int domainId) {
return Flux.fromIterable(mutationRequests)
.subscribeOn(Schedulers.boundedElastic())
.flatMap(mutationRequest -> getMutation(mutationRequest.getGameId(), mutationRequest.getTypeId(), domainId));
}
getMutation() with blocking calls, currently wrapped into a Callable:
private Mono<Mutation> getMutation(int gameId, int typeId, int domainId) {
return Mono.fromCallable(() -> {
Mutation mutation = mutationProvider.findByGameIdAndTypeId(gameId, typeId).block(); // mutationProvider.findByGameIdAndTypeId() returns Mono<Mutation>
if (mutation == null) {
throw new RuntimeException("Mutation was not found by gameId and typeId");
}
State state = stateService.getStateByIds(mutation.getId()), domainId).blockFirst(); //stateService.getStateByIds() returns Mono<State>
if (state == null || state.getValue() == null) {
log.info("Requested mutation with gameId[%s] typeId[%s] domainId[%s] is disabled. Value is null.".formatted(gameId, typeId, domainId));
return null;
}
mutation.setTemplateId(state.getTemplateId());
return (mutation);
});
}
How do I approach the getMutation() function to use reactive streams, instead of using .block() methods inside a Callable?
Basically, I first need to retrieve Mutation from DB -> then using ID of mutation, get its state from other service -> then if state and its value are not null, set templateId of state to mutation and return, or return null.
I've tried something like this:
private Mono<Mutation> getMutation(int gameId, int typeId, int domainId) {
return mutationProvider.findByGameIdAndTypeId(gameId, typeId)
.flatMap(mutation -> {
stateService.getStatesByIds(mutation.getId(), domainId).flatMap(state -> {
if (state != null && state.getValue() != null) {
mutation.setTemplateId(state.getTemplateId());
}
//TODO if state/value is null -> need to propagate further to return null instead of mutation...
return Mono.justOrEmpty(state);
});
return Mono.just(mutation);
});
}
But it's obviously incorrect, nothing is subscribed to stateService.getStatesByIds(mutation.getId()), domainId)
AND
I would like to return a null if the retrieved state of mutation or its value are null.

You are ignoring the value of the inner flatMap hence the warning.
Without trying you need something like this
private Mono<Mutation> getMutation(int gameId, int typeId, int domainId) {
return mutationProvider.findByGameIdAndTypeId(gameId, typeId)
.flatMap(mutation -> {
return stateService.getStatesByIds(mutation.getId(), domainId).flatMap(state -> {
if (state != null && state.getValue() != null) {
mutation.setTemplateId(state.getTemplateId());
return Mono.just(mutation);
}
return Mono.empty();
});
});
}
Although not sure if you could rewrite the outer flatMap not to a regular map instead and you might want to use filter and defaultIfEmpty with that as well
private Mono<Mutation> getMutation(int gameId, int typeId, int domainId) {
return mutationProvider.findByGameIdAndTypeId(gameId, typeId)
.flatMap(mutation -> {
return stateService.getStatesByIds(mutation.getId(), domainId)
.filter(state -> state != null && state.getValue() != null)
.flatMap(state -> {
mutation.setTemplateId(state.getTemplateId());
return Mono.just(mutation);})
.defaultIfEmpty(Mono.empty());
}
This is just from the top of my head and I have no idea what some of the return types are here (Flux or Mono) for your own APIs.

Related

Find null Type's name in Optional method chain(through map operation) for multilevel POJO

I have similar class structure.
class Request {
Level1 level1;
}
class Level1 {
Level2 level2;
String data;
}
class Level2 {
Level3 level3;
String data;
}
class Level3 {
Level4 level4;
String data;
}
class Level4 {
String data;
}
Request r = new Request();
r.level1 = new Level1();
r.level1.level2 = new Level2();
r.level1.level2.level3 = null;//new Level3();
//r.level1.level2.level3.level4 = new Level4();
//r.level1.level2.level3.level4.data = "level4Data";
and to get data from nested fields I do following
Benefit of using Optional being I don't have to worry about checking null at each level in object hierarchy
String level4Data = Optional.ofNullable(r)
.map(req -> req.level1)
.map(l1 -> l1.level2)
.map(l2 -> l2.level3)
.map(l3 -> l3.level4)
.map(l4 -> l4.data)
.orElse(null);
System.out.println("level4Data: " + level4Data);
but again if I want to log reason behind level4Data being null, I have to do following/I don't any better
if (level4Data == null) {
if (r == null) System.out.println("request was null");
else if (r.level1 == null) System.out.println("level1 was null");
else if (r.level1.level2 == null) System.out.println("level2 was null");
else if (r.level1.level2.level3 == null) System.out.println("level3 was null");
else if (r.level1.level2.level3.level4 == null) System.out.println("level4 was null");
else if (r.level1.level2.level3.level4.data == null) System.out.println("level4.data was null");
}
is there more elegant/efficient way of doing this as it defeats benefits of using Optional in first place
Thank you for your time and inputs
Optional doesn't have a peek method like in Stream API.
For your use case, you can write a wrapper that will do the additional job:
// logging wrapper
static <T, R> Function<T, R> logIfNull(Function<? super T, ? extends R> function, String message) {
return input -> {
R result;
if ((result = function.apply(input)) == null)
System.out.println("logIfNull :: Null found. " + message);
return result;
};
}
// usage
String level4Data = Optional.ofNullable(r)
.map(logIfNull(req -> req.level1, "req.level1"))
.map(logIfNull(l1 -> l1.level2, "l1.level2"))
.map(logIfNull(l2 -> l2.level3, "l2.level3"))
.map(logIfNull(l3 -> l3.level4, "l3.level4"))
.map(logIfNull(l4 -> l4.data, "l4.data"))
.orElse(null);

Spring Data JPA : Efficient Way to Invoke Repository Methods with Optional Parameters

I have the below Java 11 method which is invoked by the controller where ID is the required param and status,version are optional params. I had to write multiple repository methods to fetch the record based on those params. Am wondering is there a better/effiecient way to refactor this method with out the if/else ladder?
#Override
#Transactional(transactionManager = "customTransactionManager")
public Optional<String> getInformation(UUID id, Status status, Long version) {
try {
Preconditions.checkNotNull(id, ID_MUST_BE_NOT_NULL_MSG);
if (status != null && version != null) {
return repository.findByIdAndVersionAndStatus(id, version, status);
} else if (status != null) {
return repository.findFirstByIdAndStatus(id, status);
} else if (version != null) {
return repository.findFirstByIdAndVersion(id, version);
} else {
return repository.findFirstByIdOrderByIdDesc(id);
}
} catch (Exception e) {
log.error(e);
throw new CustomException(MessageFormat.format(PUBLIC_ERROR_MESSAGE, id));
}
}
You could use Specifications for that:
private Specification<YourEntity> toSpecification(UUID id, Status status, Long version) {
return (root, query, builder) -> {
Set<Predicate> predicates = new HashSet<>();
predicates.add(builder.equal(root.get("id"), id));
if (status != null) predicates.add(builder.equal(root.get("status"), status));
if (version != null) predicates.add(builder.equal(root.get("version"), version));
return builder.and(predicates.toArray(Predicate[]::new));
};
}
If you let your repository extend JpaSpecificationExecutor you can use the build specification object like so:
Specification<YourEntity> specification = toSpecification(id, status, version);
Optional<YourEntity> result = repository.findOne(specification);
When using Hibernate Metamodel Generator you can also write builder.equal(YourEntity_.id, id) instead of builder.equal(root.get("id"), id).
In addition to the accepted answer, I find Query By Examples much more intuitive and simple.
https://www.baeldung.com/spring-data-query-by-example would be a good start.
It basically creates a query based on non-null fields from your jpa entity.

How to define dependencies on two client calls in quarkus reactive programming

I have two Client APIs that return an Uni.
Uni<Customer> getCustomer(customerID)
Uni<Address> getAddress(addressID)
And I want to open a REST API
Uni<FullCustomer> getFullCustomer(String customerID)
The logic is to make the Customer Client call first. If the returned customer object has addressID then make the second Address Client call and get shipping address details. If shipping address is not available then just wrap the customer in FullCustomer object and return else wrap both customer and address in FullCustomer object and return.
I dont want to block the thread on client call (await().indefinitely()), hence i am using onItem and transfer method call. But my code returns a Uni<Uni> and i want it to return a Uni.
#GET
#Path("api/customer/{id}")
#Produces({ "application/json" })
Uni<Uni<FullCustomer>> getFullCustomer(#PathParam("id") String customerID){
Uni<Customer> customerResponse = getCustomer(customerID);
Uni<Uni<FullCustomer>> asyncResponse = customerResponse.onItem().transform(customer -> {
if (customer.getAddressId() != null) {
Uni<Address> addressResponse = getAddress(customer.getAddressId());
Uni<FullCustomer> fullCustomer = addressResponse.onItem().transform(address -> {
if (address.getShippingAddress() != null) {
return new FullCustomer(customer, address.getShippingAddress());
} else {
return new FullCustomer(customer);
}
});
}
return Uni.createFrom().item(new FullCustomer(customer));
});
return asyncResponse;
}
How can I rewrite my code so that it returns Uni keeping reactive ( async client ) calls
Got the solution. Thanks Ladicek for comments.
public Uni<FullCustomer> getFullCustomer(#PathParam("id") String customerID) {
return getCustomer(customerID)
.onItem()
.transformToUni(customer -> {
if (customer.getAddressId() != null) {
return getAddress(customer.getAddressId()).onItem().transform(address -> {
if (address.getShippingAddress() != null) {
return new FullCustomer(customer, address.getShippingAddress());
} else {
return new FullCustomer(customer);
}
});
} else {
return Uni.createFrom().item(new FullCustomer(customer));
}
});
}

Observable that combines behavior of groupby with combinelatest and exhibits "outer join" behavior

I'm trying to use reactive paradigm to create an observable that acts like a combination of "group by" and combinelatest. I have two source observables that have a shared joining key, like in the following two data structures.
class Foo
{
string Key;
string Funky;
}
class Bar
{
string Key;
string Town;
}
What I want is an observable that yields me the latest combination of these two joined on InstrumentID. The end result should look something like:
class Target
{
string Key;
string Funky;
string Town;
}
and exhibits an "outer join" like behavior, meaning the first sequence to produce a new "key" will yield a Target class with the other side being null, and then once the other side also produces the same joining key, the latest from both sides is yielded whenever there's a new value in either sequence for the given key.
Let's say your foo$ stream emits values of type Foo, and bar$ stream emits values of Bar.
Here is how you can combine them:
combineLatest([
foo$,
bar$
// use startWith(null) to ensure combineLatest will emit as soon as foo$ emits, not waiting for bar$ to emit its first value
.pipe(startWith(null))
]).pipe(
map(([foo, bar]) => ({
// always keep all properties from foo
...foo,
// only add properties from bar if it has the matching Key
...(bar && bar.Key === foo.Key ? bar : null)
}))
)
This may not be "cosher" by some standards but works for what I need it to do. Posting for anyone looking for same functionality (.NET Version of RX)
public static class Extensions
{
public static IObservable<TResult> CombineLatestGrouped<TFirst,TSecond,TKey, TResult>(
this IObservable<TFirst> first,
IObservable<TSecond> second,
Func<TFirst, TKey> firstKeySelector,
Func<TSecond, TKey> secondKeySelector,
Func<TKey,TFirst,TSecond,TResult> resultSelector)
{
var dic = new ConcurrentDictionary<TKey,Tuple<TFirst,TSecond>>();
return Observable.Create<TResult>(obs =>
{
var d1 = first
.Select(x =>
{
var key = firstKeySelector(x);
var tuple = dic.AddOrUpdate(
key,
addValueFactory: key => Tuple.Create(x, default(TSecond)),
updateValueFactory: (key, existing) => Tuple.Create(x, existing.Item2));
return resultSelector(key, tuple.Item1, tuple.Item2);
})
.Subscribe(obs);
var d2 = second
.Select(x =>
{
var key = secondKeySelector(x);
var tuple = dic.AddOrUpdate(
key,
addValueFactory: key => Tuple.Create(default(TFirst), x),
updateValueFactory: (key, existing) => Tuple.Create(existing.Item1, x));
return resultSelector(key, tuple.Item1, tuple.Item2);
})
.Subscribe(obs);
return new CompositeDisposable(d1, d2);
});
}
}
As I see it, you would like following:
a new "key" will yield a Target class with the other side being null
When left or right side emmits a NEW key (prev: null or different)
, and then once the other side also produces the same joining key,
precondition: a stream emitted a value -- other stream now emits a value and the key for left and right eq
the latest from both sides is yielded whenever there's a new value in either sequence for the given key.
emit full target (composed of left, right) on each left,right emit, when a value of left,right changes distinctly
RxJava2 solution for my assumption:
#Test
void test2() {
PublishSubject<Foo> foo$ = PublishSubject.create();
PublishSubject<Bar> bar$ = PublishSubject.create();
Observable<Target> target$ = Observable.merge(Arrays.asList(foo$, bar$))
// filter invalid values
.filter(hasId -> hasId.key() != null)
.scan(Target.NULL, (prev, change) -> {
// when prev. target and current value#key are eq -> emit composed value
if (change.key().equals(prev.key)) {
return composedTarget(prev, change);
} else if (change instanceof Foo) {
return Target.fromFoo((Foo) change);
} else if (change instanceof Bar) {
return Target.fromBar((Bar) change);
}
return prev;
}).filter(target -> target != Target.NULL)
.distinctUntilChanged();
TestObserver<Target> test = target$.test();
// emit
foo$.onNext(new Foo("123", "f1"));
// emit
bar$.onNext(new Bar("123", "f2"));
// emit
bar$.onNext(new Bar("123", "f3"));
// skipped
foo$.onNext(new Foo("123", "f1"));
// emit
foo$.onNext(new Foo("123", "f5"));
// emit
foo$.onNext(new Foo("key", "value"));
// emit
foo$.onNext(new Foo("key2", "value2"));
// emit
bar$.onNext(new Bar("bar2", "Berlin"));
// emit
foo$.onNext(new Foo("foo2", "Funkeey"));
test.assertValues(
new Target("123", "f1", null),
new Target("123", "f1", "f2"),
new Target("123", "f1", "f3"),
new Target("123", "f5", "f3"),
new Target("key", "value", null),
new Target("key2", "value2", null),
new Target("bar2", null, "Berlin"),
new Target("foo2", "Funkeey", null)
);
}
private Target composedTarget(Target prev, HasId change) {
if (change instanceof Foo) {
Foo foo = (Foo) change;
return new Target(prev.key, foo.funky, prev.town);
}
if (change instanceof Bar) {
Bar bar = (Bar) change;
return new Target(prev.key, prev.funky, bar.town);
}
return prev;
}
Domain-Classes
interface HasId {
String key();
}
static final class Foo implements HasId {
final String key;
final String funky;
Foo(String key, String funky) {
this.key = key;
this.funky = funky;
}
#Override
public String key() {
return key;
}
}
static final class Bar implements HasId {
String key;
String town;
Bar(String key, String town) {
this.key = key;
this.town = town;
}
#Override
public String key() {
return key;
}
}
static final class Target {
private static final Target NULL = new Target(null, null, null);
final String key;
final String funky;
final String town;
Target(String key, String funky, String town) {
this.key = key;
this.funky = funky;
this.town = town;
}
static Target fromFoo(Foo foo) {
return new Target(foo.key, foo.funky, null);
}
static Target fromBar(Bar bar) {
return new Target(bar.key, null, bar.town);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Target target = (Target) o;
return key.equals(target.key) &&
Objects.equals(funky, target.funky) &&
Objects.equals(town, target.town);
}
#Override
public int hashCode() {
return Objects.hash(key, funky, town);
}
#Override
public String toString() {
return "Target{" +
"key='" + key + '\'' +
", funky='" + funky + '\'' +
", town='" + town + '\'' +
'}';
}
}
Please correct my assumptions, if I am mistaken. The solution could be implemented way better in C# with pattern-matching. Actually if C# has union types like F#, that would be best.

Refactor multiple or conditions with java8

I have a scenario to refactor multiple or conditions in if block with java8
private boolean isStatusAvailable(List<Hierarchy> hierarchyList, String status, int i) {
return isContainsStatus(hierarchyList.get(i+1), status) || isContainsStatus(hierarchyList.get(i+1), ResultStatus.SUCCESS) || isContainsStatus(hierarchyList.get(i+1), ResultStatus.PEND);
}
private boolean isContains(Hierarchy hierarchy, String status) {
return hierarchy.getStatus().contains(status);
}
public enum ResultStatus {
SUCCESS,
SUCCESS_PENDING,
SUCCESS_SUMMARY,
PEND
}
Is there any way to refactor the above code in java8?
There is always the possibility to introduce a loop like:
private boolean isStatusAvailable(List<Hierarchy> hierarchyList, ResultStatus status, int i) {
Hierarchy hierarchy = hierarchyList.get(i + 1);
for (ResultStatus s : Arrays.asList(status, SUCCESS, PEND)) {
if (isContainsStatus(hierarchy, s)) {
return true;
}
}
return false;
}
If you want to use Streams then you can write:
private boolean isStatusAvailable(List<Hierarchy> hierarchyList, ResultStatus status, int i) {
Hierarchy hierarchy = hierarchyList.get(i + 1);
return Stream.of(status, SUCCESS, PEND).anyMatch(s -> isContainsStatus(hierarchy, s));
}

Resources