Springboot webflux reactor delete items from mongoDB - spring-boot

I use springboot + mongodb, and I am a beginer for webflux. I write code for CRUD. When I access delete ids in Controller, code not working because count alway return 0. Any one help me?
#ApiOperation(value = "Delete multi cities")
#DeleteMapping
public Mono<ResponseEntity<AtomicInteger>> deleteByIds(#RequestBody #NotNull Set<String> ids) {
AtomicInteger count = new AtomicInteger(0);
Flux.fromIterable(ids)
.flatMap((id) -> {
return cityService.findById(id)
.flatMap((c) -> {
count.getAndAdd(1);
return cityService.deleteById(c.getId());
});
});
log.debug("count = {}", count);
return Mono.just(ResponseEntity.ok(count));
}

The Flux is not be subscribed
you should try like this
return Flux.fromIterable(ids)
.flatMap((id) -> {
return cityService.findById(id)
.flatMap((c) -> {
count.getAndAdd(1);
return cityService.deleteById(c.getId());
});
})
.then(Mono.defer(() -> {
log.debug("count = {}", count);
return Mono.just(ResponseEntity.ok(count));
}));

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 {
// ...

Insert in DB for multiple records not working in webflux R2DBC

I am trying to insert some records in DB in one go, also tried for loop but it never happens, If I save a single record, it works
#RequiredArgsConstructor
#Service
public class UserServiceImpl {
private final UserRepository userRepo;
private final FamilyRepository familyRepo;
public Mono<ServerResponse> insertUserData(ServerRequest serverRequest) {
return serverRequest.bodyToMono(UserAndFamilyRequest.class)
// .map(userAndFamilyRequest -> {
// List<FamilyMember> list = userAndFamilyRequest.getFamilyMemberList();
// list.stream().forEach((familyMember) ->
// {
// System.out.println(familyMember.getName());
// FamilyMemberEntity familyMemberEntity = new FamilyMemberEntity();
// familyMemberEntity.setAge(familyMember.getAge());
// familyMemberEntity.setName(familyMember.getName());
// familyRepo.save(familyMemberEntity);//doesn't work either
// try{
// Thread.sleep(2000);
// }catch(Exception ex){
//
// }
//
// });
// return userAndFamilyRequest;
// })
.map(userAndFamilyRequest -> {
List<FamilyMember> list = userAndFamilyRequest.getFamilyMemberList();
var entityList = list.stream().map(familyMember -> {
FamilyMemberEntity familyMemberEntity = new FamilyMemberEntity();
familyMemberEntity.setName(familyMember.getName());
familyMemberEntity.setAge(familyMember.getAge());
return familyMemberEntity;
}).collect(Collectors.toList());
familyRepo.saveAll(entityList);//doesn't work
return userAndFamilyRequest;
})
.flatMap(userAndFamilyRequest -> {
UserEntity userEntity = new UserEntity();
User user = userAndFamilyRequest.getUser();
userEntity.setSeats(userAndFamilyRequest.getFamilyMemberList().size());
userEntity.setAge(user.getAge());
userEntity.setName(user.getName());
return userRepo.save(userEntity);
})
// .flatMap(userAndFamilyRequest -> {
// FamilyMember familyMember = userAndFamilyRequest.getFamilyMemberList().get(0);
// FamilyMemberEntity familyMemberEntity = new FamilyMemberEntity();
// familyMemberEntity.setAge(familyMember.getAge());
// familyMemberEntity.setName(familyMember.getName());
// return familyRepo.save(familyMemberEntity);//single save works
// })
.flatMap(userEntity -> ServerResponse.created(URI.create("users"+userEntity.getId()))
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(userEntity));
}
}
no error in the console
add try {} catch in function save data, so you can know what error you face it
Well I found the answer by myself, the familyRepo.save inside map will not work by itself because it returns a Flux which will execute only when subscribed, replacing it with flatMap(which automatically subscribes) resolved the issue.

return Mono.error inside Map Spring Webflux

For Matching condition, return Mono.error from code , but gives compilation error. I commented out
//return Mono.error(new RuntimeException("User Phone Exists already"));
Result:
Compilation Error: Required Type Mono<EventSlotBook>, Provided
Mono<Object>
Code:
public Mono<EventSlotBook> getEventSlotBookWithAppointmentId(EventSlotBook eventSlotBookEntity) {
Query query = new Query();
query.addCriteria(
new Criteria().andOperator(
Criteria.where("eventId").is(eventSlotBookEntity.getEventId()),
Criteria.where("eventConfigId").is(eventSlotBookEntity.getEventConfigId()),
Criteria.where("eventSlotId").is(eventSlotBookEntity.getEventSlotId())));
return this.reactiveMongoTemplate.findOne(query, EventSlotBook.class)
.map(eventSlotBookEntityFromDb -> {
EventSlotBook eventSlotNewEntity = eventSlotBookEntityFromDb.toBuilder().build();
if(eventSlotNewEntity.getEventUsers() != null) {
for(EventUser eventUserIno:eventSlotNewEntity.getEventUsers()) {
if(eventUserIno.getPhoneNumber().equalsIgnoreCase(eventSlotBookEntity.getEventUser().getPhoneNumber())){
//return Mono.error(new RuntimeException("User Phone Exists already"));
}
}
}
int maxTokenVal = Integer.valueOf(eventSlotNewEntity.getMaxTokenVal()).intValue() + 1;
EventUser eventUser = new EventUser(eventSlotNewEntity.getEventUser().getName(),eventSlotNewEntity.getEventUser().getPhoneNumber(),String.valueOf(maxTokenVal));
eventSlotNewEntity.getEventUsers().add(eventUser);
eventSlotNewEntity.setMaxTokenVal(String.valueOf(maxTokenVal));
eventSlotNewEntity.setEventUser(eventUser);
return eventSlotNewEntity;
//return Mono.error(new RuntimeException("Ts"));
})
.switchIfEmpty(getEventSlotBook(eventSlotBookEntity));
}
caller of Method : I should handle Mono.error and return to rest API that user already exists ?. Please help on this
public Mono<EventSlotBookRequestDto> saveEventSlotBook(Mono<EventSlotBookRequestDto> eventSlotBookRequestDtoMono){
log.info("Start::SaveEventSlotBook");
Mono<EventSlotBookRequestDto> eventDtoSaved =
eventSlotBookRequestDtoMono.map(AppUtils::dtoToEntity)
.flatMap(eventSlotEntity -> getEventSlotBookWithAppointmentId(eventSlotEntity))
.doOnNext(eventSlotEntityBeforeSave -> {
log.info("####BeforeSave::{}",eventSlotEntityBeforeSave);
})
.flatMap(eventSlotBookrepository::save)
.doOnNext( eventSlotBookAfterSave -> {
log.info("####AfterSave::{}",eventSlotBookAfterSave);
})
.map(AppUtils::entityToDto);
log.info("End::SaveEventSlotBook");
return eventDtoSaved;
}
map is used to apply a synchronous function to each item, therefore you can't return Mono from it. To return an error from map you could just throw an exception and error signal will be emited. As an alternative you you use handle operator and use SynchronousSink to emit next or error signal.
But in your case you need to use flatMap instead because saveEventSlotBook returns Mono and should be transformed asynchronously.

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);
});
}

Validation Webflux ReactiveMongo

I wanna ask about Webflux using Spring-data-mongoreactive. how you perform checking data exist? I've been search for 3 days but still not found the way.
public Mono<TenantRegistrationModel> registration(TenantRegistrationModel registrationModel1) {
registrationModel1.beforeRegistration();
return Mono.just(registrationModel1).flatMap(registrationModel -> {
TenantEntity tenantEntity = new TenantEntity();
tenantEntity.setFullName(registrationModel.getFullName());
tenantEntity.setEmail(registrationModel.getEmail());
tenantEntity.setCountry(registrationModel.getCountry());
tenantEntity.setCity(registrationModel.getCity());
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
tenantEntity.setPassword(passwordEncoder.encode(registrationModel.getPassword()));
tenantEntity.setUpdatedAt(new Date());
tenantEntity.setCreatedAt(new Date());
Mono<TenantEntity> tenantEntityMono = this.tenantService.save(tenantEntity);
return this.tenantRepository.findByEmail(registrationModel.getEmail()).next().doOnNext(tenantEntity1 -> {
if (tenantEntity1.getEmail() != null)
throw new ApiExceptionUtils("email already eixst", HttpStatus.UNPROCESSABLE_ENTITY.value(), StatusCodeUtils.VALIDATION_FAIL);
}).switchIfEmpty(tenantEntityMono).map(tenantEntity1 -> {
registrationModel.setId(tenantEntity1.getId());
return registrationModel;
});
});
}
I want to validate 2 query from mongoreactive and comparing the result.

Resources