Search code examples
mutiny

How to use condition and multiple steps in a reactive function


I'm currently building a reactive app using kotlin quarkus and mutiny, being new to reactive programming i wonder what's the best way to handle the following workflow :

  • try to find an item in the database (dynamodb)
  • if it exists return a value indicating this to the caller
  • if it does not exists call the save service
  • call an external service to get some data
  • update the item in database with those data from the external service
  • return a value indicating that the object has been created

Here's my code for now :

fun createCard(creationOrder: CreationOrder): Uni<CardCreationResult> {
    return creationOrdersRepository.findByOrderId(creationOrder.orderId)
        .onItem().transform {item ->
            if (item != null) {
                CardCreationResult.AlreadyCreated
            } else {
                creationOrdersRepository.save(creationOrder)

                //TODO external webservice call
                
                val cardNumber = UUID.randomUUID().toString()
                creationOrdersRepository.updateCardNumberAndStatus(externalServiceCallResult)
                CardCreationResult.Created
            }
        }
}

This method will eventually be called by a rest endpoint.

creationOrdersRepository.save and creationOrdersRepository.updateCardNumberAndStatus returns a CompletableFuture (i'm using the quarkus amazon dynamodb client).

Is this the right way to do it ? Should i wrap the save and updateCardNumberAndStatus results in Uni (i have been trying to but keep getting type error) ?


Solution

  • I don't believe your code does what you expect. If I understand correctly you need to "save" and "update" before emitting your result. So, you need something like (in Java, as my Kotlin is not great):

    return creationOrdersRepository.findByOrderId(creationOrder.orderId)
      // We will be returning a Uni as the save, update and the web service are 
      // async
      .onItem().transformToUni(item -> {
        if (item != null) {
          return Uni.createFrom().item(CardCreationResult.AlreadyCreated);
        } else {
          // Create an Uni from the save operation
          Uni<Void> save = Uni.createFrom().completionStage(() -> 
            creationOrdersRepository.save(creationOrder));
          // This uni represents the invocation of the remote service
          Uni<String> webService = callWebService();
    
          // we chain the operations save -> web service -> update -> Created
          return save
            .chain(webService)
            .chain(externalServiceCallResult -> 
              Uni.createFrom().completionStage(() -> creationOrdersRepository
                      .updateCardNumberAndStatus(externalServiceCallResult)
            )        
            .replaceWith(CardCreationResult.Created);
        }