javamutinyquarkus-hibernate-reactive

How do I conditionally post using hibernate-reactive based on the result of a different NamedQuery


I have a POST operation that should only occur if the result of another NamedQuery has a row count Greater Than ">" than a known value.

I am new to Mutiny and moreso hibernate-reactive. My current catastrophe looks like this:

package org.electricaltrainingalliance.attendees.selections.boundary;

import java.util.UUID;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import org.electricaltrainingalliance.attendees.selections.entity.AttendeeSelection;
import org.hibernate.reactive.mutiny.Mutiny.SessionFactory;
import org.jboss.logging.Logger;
import org.jboss.logging.Logger.Level;
import org.jboss.resteasy.reactive.RestPath;

import io.smallrye.mutiny.Uni;

@Path("selections")
@ApplicationScoped
@Produces("application/json")
@Consumes("application/json")
public class AttendeeSelectionsService {

    private static final Logger LOGGER = Logger.getLogger(AttendeeSelectionsService.class.getName());

    @Inject
    SessionFactory sf;

    @POST
    public Uni<Response> create(AttendeeSelection selection) {
        if (selection == null || !selection.isValid()) {
            return Uni.createFrom()
                    .item(Response.status(400)
                            .entity("The Selection is not valid, please refer to the API Documentation.")::build);
        }

        Uni<Boolean> seatsAvailable = sf.withSession((s) -> s
                .createNamedQuery(AttendeeSelection.findByOfferingId, AttendeeSelection.class)
                .setParameter("offeringId", selection.getOffering().getOfferingId())
                .getResultList()).onItem()
                .transform(entity -> entity.size() >= selection.getOffering().getCapacity() ? Boolean.FALSE : Boolean.TRUE);

        seatsAvailable.onItem().transformToUni(result -> {
            if (!result) {
                return Uni.createFrom()
                        .item(Response.status(409)
                                .entity("The Offering is no longer available.")::build);
            } else {
                sf.withTransaction((s, t) -> s.persist(selection))
                        .replaceWith(Response.ok(selection).status(Status.CREATED)::build)
                        .onFailure().transform(failure -> {
                            if (failure.getMessage().toLowerCase().contains("uk_unique_selection")) {
                                throw new WebApplicationException(
                                        "A Selection with this information already exists.",
                                        Status.CONFLICT);
                            } else {
                                LOGGER.log(Level.DEBUG, failure.getMessage(), failure);
                                throw new WebApplicationException(failure.getMessage(),
                                        Status.INTERNAL_SERVER_ERROR);
                            }
                        });
            }
        });
        
    }
...
}

Essentily, I have a query that returns a list of elements. The item being created, has a value that handles the maximum allowed.

I would like to:

I have tried an .await() on the NamedQuery to get a Boolean. I get the error This method should exclusively be invoked from a Vert.x EventLoop thread; currently running on thread 'executor-thread-0

I have tried in place of .transform as well as .transformToUni but it provides the context error This method must return a result of type Uni<Response> of which all paths do, confusingly.


Solution

  • Much like promises in JS, passing the Uni is part of the issue, and I need to return from within the chain.

    return seatsAvailable.onItem().transformToUni(result -> {
    

    I could then handle the check on the result as a boolean and continue by returning the following:

    return sf.withTransaction((s, t) -> s.persist(selection))