Spring Mono ZIP using args as argument - spring

Anything wrong with the below code, the lambda function is not executing. How to use the args ? Any help would be appreciated. The lambda function is not executing
Mono<String> test = Mono.just("test");
Mono<String> testtwo = Mono.just("testTwo");
Mono<String> str = Mono.zip(args -> {
return (String) args[0];
});
System.out.println(str); // This is printing MonoEmpty

Related

Mono<Object> being returned instead of Mono<ResponseEntity> when mapping (Java 8)

Trying to practice reactive coding for an API but I'm struggling to understand what I'm doing wrong when using flatMap() and map() to cast to a ResponseEntity object. The error mentions that the code is returning a Mono<Object> and cant be cast/transformed into a Mono<ResponseEntity<>>.
Public Mono<ResponseEntity<?> deleteEndpoint(String someId) {
return db.existsById(someId).flatMap(exists -> {
if (!exists) return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
else {
Mono<Boolean> deletedStuff1 = someFunct();
Mono<Boolean> deletedStuff2 = anotherFunct();
Mono<Tuple2<Boolean, Boolean>> deletedStuff = Mono.zip(deletedStuff1, deletedStuff2);
return deletedStuff.then(Mono.just(ResponseEntity.status(NO_CONTENT).build());
}
});
}
All help is appreciated
From .flatMap() you must return Publisher, not actual object
In this if statement you return ResponseEntity instead of Mono<ResponseEntity>
So, just wrap it with Mono
if (!exists) {
return Mono.just(ResponseEntity.status(HttpStatus.BAD_REQUEST).build());
} else {
// ...

Spring unit tests [webflux, cloud]

I am new to the topic of unit testing and my question is whether I should perform the test as such of each line of code of a method or in what ways I can perform these tests to have a good coverage, if also, should exceptions be evaluated or not?
If for example I have this service method that also uses some helpers that communicate with other microservices, someone could give me examples of how to perform, thank you very much.
public Mono<BankAccountDto> save(BankAccountDto bankAccount) {
var count = findAccountsByCustomerId(bankAccount.getCustomerId()).count();
var customerDto = webClientCustomer
.findCustomerById(bankAccount.getCustomerId());
var accountType = bankAccount.getAccountType();
return customerDto
.zipWith(count)
.flatMap(tuple -> {
final CustomerDto custDto = tuple.getT1();
final long sizeAccounts = tuple.getT2();
final var customerType = custDto.getCustomerType();
if (webClientCustomer.isCustomerAuthorized(customerType, accountType, sizeAccounts)) {
return saveBankAccountAndRole(bankAccount);
}
return Mono.error(new Exception("....."));
});
}
EDIT
public Mono<BankAccountDto> save(BankAccountDto bankAccount) {
var count = findAccountsByCustomerId(bankAccount.getCustomerId()).count();
var customerDto = webClientCustomer
.findCustomerById(bankAccount.getCustomerId());
return customerDto
.zipWith(count)
.flatMap(tuple -> {
final var customDto = tuple.getT1();
final var sizeAccounts = tuple.getT2();
final var accountType = bankAccount.getAccountType();
// EDITED
return webClientCustomer.isCustomerAuthorized(customDto, accountType, sizeAccounts)
.flatMap(isAuthorized -> {
if (Boolean.TRUE.equals(isAuthorized)) {
return saveBankAccountAndRole(bankAccount);
}
return Mono.error(new Exception("No tiene permisos para registrar una cuenta bancaria"));
});
});
}
Given that you want to unit test this code, you would need to mock dependencies such as webClientCustomer.
Then you should always test whatever are the relevant paths within the code. Looking at your code I only see three relevant ones to be tested:
the method returns an empty Mono if webClientCustomer.findCustomerById(bankAccount.getCustomerId()); returns an empty Mono;
saveBankAccountAndRole(bankAccount) is called and your save() method actually returns whatever saveBankAccountAndRole(bankAccount) returns. This would should happen if webClientCustomer.isCustomerAuthorized(customerType, accountType, sizeAccounts) is true;
the method returns an exception if webClientCustomer.isCustomerAuthorized(customerType, accountType, sizeAccounts) is false.

Skip chain of CompletableFuture based on a specific condition

There are several CompletionFutures methods that I'd like to chain. The problem is that I want to skip other chain and go to last method of the pipeline if some condition is met. Mentioned as comment in the below code. For example:
public CompletableFuture<Void> process() {
CompletableFuture<Set<String>> service1 = new CompletableFuture();
return service1
.thenCompose(data -> {
if (!data.isEmpty()) {
SomeObject obj = prepareSomeObject();
obj.addAll(data.stream().collect(Collectors.toSet()));
//Here want to check some condition and if it meets, then go to last method of the pipeline
//Otherwise continue with the current flow
CompletableFuture<String> someFuture = new CompletableFuture<>();
CompletableFuture<String> someOtherFuture= new CompletableFuture<>();
return CompletableFuture.allOf(someFuture, someOtherFuture)
.thenApply(aVoid -> {
String user = someFuture.join();
String unit = someOtherFuture.join();
obj.setUnit(unit);
obj.setUser(user)
return obj;
})
.thenCompose(this::someMethod)
.thenCompose(this::someOtherMethod); // last method in the pipeline
}
return completedFuture(null);
});
}
I have tried something like this, Please critique on the right way to do it.
public CompletableFuture<Void> process() {
CompletableFuture<Set<String>> service1 = new CompletableFuture();
return service1
.thenCompose(data -> {
if (!data.isEmpty()) {
SomeObject obj = prepareSomeObject();
obj.addAll(data.stream().collect(Collectors.toSet()));
if(condition not match) { //condition match
CompletableFuture<String> someFuture = new CompletableFuture<>();
CompletableFuture<String> someOtherFuture= new CompletableFuture<>();
return CompletableFuture.allOf(someFuture, someOtherFuture)
.thenApply(aVoid -> {
String user = someFuture.join();
String unit = someOtherFuture.join();
obj.setUnit(unit);
obj.setUser(user)
})
.thenCompose(this::someMethod)
.thenCompose(this::someOtherMethod); // last method in the pipeline
} else return someOtherMethod(obj);
}
return completedFuture(null);
});
}

What's the correct way to get the response body from a WebClient in an error case?

I'm new to WebClient and reactive programming. I want to get the response body from a request. In case of an error the http-code, headers and body must be logged, but the body should still be returned.
After lots of digging and googling I found two solutions. But both look over complicated to me. Is there a simpler solution?
Staying with a Mono I found this solution:
public Mono<String> log(ProtocolLine protocolLine) {
return webClient.post()
.uri("/log")
.body(BodyInserters.fromObject(protocolLine))
.exchange()
.flatMap(clientResponse -> {
Mono<String> stringMono = clientResponse.bodyToMono(String.class);
CompletableFuture<String> stringCompleteFuture = new CompletableFuture<String>();
Mono<String> bodyCompletedMono = Mono.fromFuture(stringCompleteFuture);
if (clientResponse.statusCode().isError()) {
stringMono.subscribe(bodyString -> {
LOGGER.error("HttpStatusCode = {}", clientResponse.statusCode());
LOGGER.error("HttpHeaders = {}", clientResponse.headers().asHttpHeaders());
LOGGER.error("ResponseBody = {}", bodyString);
stringCompleteFuture.complete(bodyString);
});
}
return bodyCompletedMono;
});
}
Based on Flux it takes less code. But I think I should not use Flux if I know that there will be only one result.
public Flux<String> log(ProtocolLine protocolLine) {
return webClient.post()
.uri("/log")
.body(BodyInserters.fromObject(protocolLine))
.exchange()
.flux()
.flatMap(clientResponse -> {
Flux<String> stringFlux = clientResponse.bodyToFlux(String.class).share();
if (clientResponse.statusCode().isError()) {
stringFlux.subscribe(bodyString -> {
LOGGER.error("HttpStatusCode = {}", clientResponse.statusCode());
LOGGER.error("HttpHeaders = {}", clientResponse.headers().asHttpHeaders());
LOGGER.error("ResponseBody = {}", bodyString);
});
}
return stringFlux;
});
}
both solutions are ugly and wrong. You should almost never subscribe in the middle of a reactive pipeline. The subscriber is usually the calling client, not your own application.
public Mono<String> log(ProtocolLine protocolLine) {
return webClient.post()
.uri("/log")
.body(BodyInserters.fromObject(protocolLine))
.exchange()
.flatMap(clientResponse -> clientResponse.bodyToMono(String.class)
.doOnSuccess(body -> {
if (clientResponse.statusCode().isError()) {
log.error("HttpStatusCode = {}", clientResponse.statusCode());
log.error("HttpHeaders = {}", clientResponse.headers().asHttpHeaders());
log.error("ResponseBody = {}", body);
}
}));
}
Here you can see the way of thinking. We always take our clientResponse and map its body to a string. We then doOnSuccess when this Mono is consumed by the subscriber (our calling client) and check the status code if there is an error and if that is the case we log.
The doOnSuccess method returns void so it doesn't "consume" the mono or anything, it just triggers something when this Mono says it "has something in itself", when it's "done" so to speek.
This can be used with Flux the same way.

How to make async call to api (http). in c#. inside a Task

I am developeing a chatbot using microsoftbotframmwok where I have some requirement to make a call from my task to an api(httpclient). but it is not working. when i test the api from an stand alone console application in side main method it works. but in my application it doesn't work.
I tried to call an api from an simple method without task but when it makes a cal its basically halts or stucked somewhere, i converted my function into task and while making an api call i used await keyword to call it asynchronously but it is returning error, while reading it not the result.
here is the my code which make an api call
private async Task<String> getProblem(IDialogContext context)
{
var response = "Thannks for contacting..";
//here some code logix..
SnowApiClient client = new SnowApiClient(Url, UserId, ApiPassword);
IncidentRequestPayload payload = new IncidentRequestPayload();
payload.caller_id = "tet111";
payload.assignment_group = "it";
payload.category = "complaint";
payload.u_feedback_type = "Praise";
payload.service_offering = "Application Management";
payload.priority = 2;
payload.short_description = "computer battery is dead";
payload.comments = String.Empty;
ApiResponse objResponse = await client.CreateIncident(payload);
//objResponse.payload.number;
return response;
}
//code for CreateIncident...in Api project librarary
public async Task<ApiResponse> CreateIncident(IncidentRequestPayload payload)
{
var incidentRequest = new ApiRequest { method = CreateIncidentMethod, payload = payload };
var createResult = await ExecuteRequest(incidentRequest);
return await ReadIncident(createResult.payload.incNumber);
}
public async Task<ApiResponse> ReadIncident(string number)
{
var incidentRequest = new ApiRequest { method = ReadIncidentMethod, payload = new RequestPayload { number = number } };
return await ExecuteRequest(incidentRequest);
}
private async Task<ApiResponse> ExecuteRequest(ApiRequest requestObject)
{
HttpResponseMessage response = await _client.PostAsJsonAsync("/SRintegratedAPI.rest", requestObject);
ApiResponse responseObject = null;
if (response.IsSuccessStatusCode)
{
responseObject = await response.Content.ReadAsAsync<ApiResponse>();
}
else
{
throw new System.Net.WebException(response.ReasonPhrase);
}
if (responseObject.result != "ok")
{
throw new System.Net.WebException(responseObject.message);
}
return responseObject;
}
I don't understand how and where do i used async/await here in basicalaly in my getProblem function.
please help

Resources