how to get Entity type from Mono<Entity>? - mono

how correctly operate getter/setter with mono object
Mono<Product> productMono = webClient.get()
.uri(catalogUrl + "/{productId}", productId)
.retrieve()
.bodyToMono(Product.class)
.onErrorResume(err -> Mono.empty())
.switchIfEmpty(Mono.error(new NoSuchProductException()));
then i want to transform my "productMono" POJO to product. thnak you for answers.

The correct way to access to Product instance is using map() operation like this:
#GetMapping("/products/{productId}")
public Mono<MappedProduct> getProduct(#PathVariable("productId") String productId) {
return webClient.get()
.uri(catalogUrl + "/{productId}", productId)
.retrieve()
.bodyToMono(Product.class)
.onErrorResume(err -> Mono.empty())
.switchIfEmpty(Mono.error(new NoSuchProductException()));
.map(product -> productMapper(product)); // <-- here you can handle Product instance
}

Related

Webflux/Reactor makes two API calls instead of one

I have a webflux application. In one place I make 3 API calls. Every call depends on result of previous call.
Mono<List<Book>> books = bookService.find(params); //1st API call
Mono<List<User>> users = books.flatMapMany(Flux::fromIterable)
.map(User::getId)
.collectList()
.map(allUserIds -> userService.findUsers(allUserIds)) //2nd API call
.flatMap(mono -> mono);
Mono<List<User>> parents = users.flatMapMany(Flux::fromIterable)
.filter(user -> user.getParentId() != null)
.map(User::getParentId)
.collectList()
.map(parentIds -> userService.findUsers(parentIds)) //3rd API call
.flatMap(mono -> mono);
Mono<List<User>> usersWithParents = Flux.merge(users, parents)
.flatMapIterable(Function.identity())
.collectList();
This code works, but I get 4 API calls in total. I get two API calls for 2nd step.
I assume that reactor makes 1 call to calculate parents variable and 2n call for Flux.merge(users, parents).
How can I update the call to avoid extra API call?
I think you should do smth like this
public Mono<List<User>> getAllUsers(){
Mono<List<String>> booksMono = bookService.find(params)
.flatMapMany(Flux::fromIterable)
.map(User::getId)
.flatMap(book->userService.findUsers(allUserIds))
.collectList()
.flatMap(users->Mono.zip(users, getParents(users)))
.flatMap(zippedUsers-> Stream.concat(zippedUsers.getT1(), zippedUsers.getT2()));
}
private Mono<List<User>> getParents(List<User> users){
return Flux.fromIterable(users)
.filter(user -> user.getParentId() != null)
.map(User::getParentId)
.collectList()
.map(parentIds -> userService.findUsers(parentIds));
}
Found a solution. We need to combine 2nd and 3rd API calls into one chain with flatmap:
Mono<List<Book>> books = bookService.find(params); //1st API call
Mono<List<User>> users = books.flatMapMany(Flux::fromIterable)
.map(User::getId)
.collectList()
.map(allUserIds -> userService.findUsers(allUserIds)) //2nd API call
.flatMap(mono -> mono);
.flatMap(children -> {
List<Long> parentIds = children.stream()
.filter(child -> child.getParentId() != null)
.map(User::getParentId)
.collect(toList());
return userService.findUsers(parentIds);
});

Optaplanner contraint streams - how to check mutliple resources

My domain is:
Requirement with some Variants of its realization
Any Variant need multiple resources with amount
Any Resource has limit
I try to write conflict method
constraintFactory
.from(Requirement::class.java)
.groupBy(Requirement::variant::resourceUsageList, sum(Resource::amount))
...
but is doesn't work
How can I get all used resource with its used amount and compare it with resources limit?
I think I need something like flatmap after from.
First make sure that your List<Resource> on your #PlanningSolution has a #ProblemFactCollectionProperty, so from(Resource.class) works.
Then I see multiple ways of doing it:
Proposal A)
from(Requirement.class)
.join(Resource.class) // Bi<Requirement, Resource>
.groupBy((requirement, resource) -> resource, sum((requirement, resource) -> requirement.variant.resourceUsage[resource.index]))
...
The downside is that this proposal A creates a Cartesian Product, so it can be costly memory wise if you have 100 resources and 10 000 requirements.
Propsal B)
.from(Requirement::class.java)
.groupBy(Requirement::variant::resourceUsageList, new MyResourceUsageSumCollector(...))
...
For MyResourceUsageSumCollector, which sums for each resource, look at this sum which sums for just one long for inspiration:
public static <A> UniConstraintCollector<A, ?, Long> sumLong(ToLongFunction<? super A> groupValueMapping) {
return new DefaultUniConstraintCollector<>(
() -> new long[1],
(resultContainer, a) -> {
long value = groupValueMapping.applyAsLong(a);
resultContainer[0] += value;
return () -> resultContainer[0] -= value;
},
resultContainer -> resultContainer[0]);
}

Functional Programming with kotlin - avoiding var

I am working with kotlin and functional programming to develop an api.I really could not figure out whether did i break any FP rules here by using here.
I have a following function which gives me customerNumber and bunch of other fields.
data class CustomerInfo(val customerNumber:String?=null,val accountNumber:String?=null,val email:String?=null)
and I have function with lot of conditions but conditions are same for all fields
fun getCustomerInfo(someDto:SomeDto,someOtherDto:SomeOtherDto,oneMoreDto:OneMoreDto):CustomerInfo
{
var customerNumber = someDto.id
var accountNo = someDto.accountNumber
var email = someDto.email
if(someCondition())
{
customerNumber= someOtherDto.id
accountNo = someOtherDto.accountNo
email = someOtherDto.email
}else if(someOtherConditiion)
{
customerNumber= oneMoreDto.id
accountNo = oneMoreDto.accountNo
email = oneMoreDto.email
}
//and many more conditions like this
return CustomerInfo(customerNumber,accountNo,email)
}
Is using var inside a functions is wrong?How can write this function without using var's here ?
I know i can return the dto every-time directly once the condition met,but i feel like using same dto in 10 conditions?Any help would be appreciated
There is nothing technically wrong in using var, because you are in a local scope of a function.
But you could avoid lots of boilerplate code like:
fun getCustomerInfo(someDto:SomeDto,someOtherDto:SomeOtherDto,oneMoreDto:OneMoreDto):CustomerInfo
{
return when {
someCondition() -> CustomerInfo(someOtherDto.id, someOtherDto.accountNumber, someOtherDto.email)
someOtherConditiion() -> CustomerInfo(oneMoreDto.id, oneMoreDto.accountNumber, oneMoreDto.email)
else -> CustomerInfo(someDto.id, someDto.accountNumber, someDto.email)
}
}
If all your (different) DTO's gets generated you could consider creating mapper extension functions for all of them:
// top-level functions
fun SomeDto.toConsumerInfo(): CustomerInfo = ConsumerInfor(id, accountNumber, email)
fun SomeOtherDto.toConsumerInfo(): CustomerInfo = ConsumerInfor(id, accountNumber, email)
fun OneMoreDto.toConsumerInfo(): CustomerInfo = ConsumerInfor(id, accountNumber, email)
// and more for other DTO's you want to map
Then you could use them like:
fun getCustomerInfo(someDto:SomeDto,someOtherDto:SomeOtherDto,oneMoreDto:OneMoreDto):CustomerInfo {
return when {
someCondition() -> someOtherDto.toConsumerInfo()
someOtherConditiion() -> oneMoreDto.toConsumerInfo()
else -> someDto.toConsumerInfo()
}

Mono.zip with null

My code:
Mono.zip(
credentialService.getCredentials(connect.getACredentialsId()),
credentialService.getCredentials(connect.getBCredentialsId())
)
.flatMap(...
From the frontend we get connect object with 2 fields:
connect{
aCredentialsId : UUID //required
bCredentialsId : UUID //optional
}
So sometimes the second line credentialService.getCredentials(connect.getBCredentialsId())) can return Mono.empty
How to write code to be prepared for this empty Mono when my second field bCredentialsId is null?
What should I do? In case of empty values return Mono.just(new Object) and then check if obj.getValue != null??? I need to fetch data from DB for 2 different values
The strategy I prefer here is to declare an optional() utility method like so:
public class Utils {
public static <T> Mono<Optional<T>> optional(Mono<T> in) {
return in.map(Optional::of).switchIfEmpty(Mono.just(Optional.empty()));
}
}
...which then allows you to transform your second Mono to one that will always return an optional, and thus do something like:
Mono.zip(
credentialService.getCredentials(connect.getACredentialsId()),
credentialService.getCredentials(connect.getBCredentialsId()).transform(Utils::optional)
).map(e -> new Connect(e.getT1(), e.getT2()))
(...assuming you have a Connect object that takes an Optional as the second parameter of course.)
An easier way is using mono's defaultIfEmpty method.
Mono<String> m1 = credentialService.getCredentials(connect.getACredentialsId());
Mono<String> m2 = credentialService.getCredentials(connect.getBCredentialsId()).defaultIfEmpty("");
Mono.zip(m1, m2).map(t -> connectService.connect(t.getT1(), t.getT2()));
Explanation: if m2 is null then get empty string as a default value instead of null.
Instead of using .zip here, I would work with a nullable property of Connect and use .flatMap in combination with .switchIfEmpty for it.
Kotlin-Version:
val aCredentials = credentialService.getCredentials(connect.getACredentialsId())
credentialService.getCredentials(connect.getBCredentialsId())
.flatMap { bCredentials -> aCredentials
.map { Connect(it, bCredentials)}
.switchIfEmpty(Connect(null, bCredentials))
}
.switchIfEmpty { aCredentials.map { Connect(it, null) } }

How to access columns from an IQueryable when dynamically constructed?

I am using the System.Linq.Data library provided here - http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
I have the following query which works great and returns an Iqueryable
IQueryable customer =
ctx.Customers.Where(cust => true).Select("new("Name,Address")");
However, how do I access these returned columns? I cannot access them using a lambda expression as follows:
var test = customer.Where(cust=>cust.Name == "Mike").First();
"cust.Name" in the above case cannot be resolved. It does not exist in the list of methods/properties for "cust".
Am i assuming something wrong here. I understand that I am working with an anonymous type. Do I have to create a DTO in this case?
For any IQueryable you have property called ElementType.
You can use it to get the properties as explained below
IQueryable query = from t in db.Cities
selec new
{
Id = t.Id,
CityName = t.Name
};
if(query!=null)
{
Type elementType = query.ElementType;
foreach(PropertyInfo pi in elementType.GetProperties())
{
}
}
Try foreach loop:
var a = _context.SENDERS.Select(x=>new { Address=x.ADDRESS, Company=x.COMPANY });
foreach(var obj in a)
{
Console.WriteLine(obj.Address);
Console.WriteLine(obj.Company);
}