Search code examples
reactive-programmingquarkusquarkus-panachemutiny

Mutiny reactive - persist to DB if upstream is not null


I working on a Quarkus + MongoDB Reactive+ Mutiny application. I have a Person object and Event Object. I am creating a new event for a person. My uri looks like this

POST /person/{personId}/event

I need to first check if the person exists in MongoDB. If the person exists then save event. If person does not exist then create a Error Status and return. I am tried everything but I am stuck and getting error that required return type is Uni but required type is Uni. I tried with transformToUni as well but it did not work. Also tried few other ways like onItemOrFailure() etc. but nothing seems to work. enter image description here

Here's the full Code.

public class EventResource {

    @Inject
    EventRepository eventRepository;

    @Inject
    PersonRepository personRepository;

    @POST
    @Path("/{person_id}/event")
    public Uni<Response> create(Event event, @PathParam("person_id") String personId){
        //Check if personId exist.
        Uni<Person> uniPerson = personRepository.getPersonById(personId);

        //THIS WORKS BUT ON FAILURE IS TREATED WHEN ERROR IS RAISED FOR EeventRepository.craete() and not if person is not found.
         /*return uniPerson.onItem().ifNotNull()
                                    .transformToUni(pid -> eventRepository.create(event, pid.getId()))
                                    .onItem().transform(e -> Response.ok().entity(e).build())

                .onFailure()
                .recoverWithItem(f-> {
                    AStatus status =  createErrorStatus(f.getMessage());
                    return Response.serverError().entity(status).build();
                });
*/
        Uni<Response> eventResp = uniPerson.onItem().transform(person -> {
                                                        if(person==null)
                                                            return Response.serverError().build();
                                                        else{
                                                          return    eventRepository.create(event, person.getId())
                                                                  .onItem().transform(event1 -> Response.ok(event1).build());

                                                        }


                                                    });

        return eventResp;


    }

Solution

  • You can use mutiny ifNull:

        @POST
        @Path("/{person_id}/event")
        public Uni<Response> create(Event event, @PathParam("person_id") String personId){
             return personRepository
                     .getPersonById(personId)
                     .onItem().ifNotNull().transformToUni(person -> createEvent(event, person))
                     .onItem().ifNull().continueWith(this::personNotFound)
                     // This onFailure will catch all the errors
                     .onFailure()
                        .recoverWithItem(f-> {
                             AStatus status =  createErrorStatus(f.getMessage());
                             return Response.serverError().entity(status).build();
                        });
        }
    
    
        private Uni<Response> createEvent(Event event, Person person) {
            return eventRepository
                .create(event, person.getId())
                .map( e -> Response.ok().entity(e).status(CREATED).build())
        }
    
        private Response personNotFound() {
            return Response.serverError().build();
        }
    

    The error you are seeing is because when the item is not null, you are returning a Uni<Uni<Response>>. This is one way to fix it:

    Uni<Response> eventResp = uniPerson
        .chain(person -> {
            if (person==null)
                return Uni.createFrom().item(Response.serverError().build());
            else {
                return eventRepository
                    .create(event, person.getId())
                    .map(event1 -> Response.ok(event1).build());
            }
        });
    

    I'm using map and chain because they are shorter, but you can replace them with onItem().transform(...) and onItem().transformToUni(...).