technazi
technazi

Reputation: 958

How to use Mono and Flux in handler functions with Spring WebFlux Reactive ways

Check for -->

My model looks something like this.

@Document
public class PlanDetails {

    @Id
    private String id;
    private String name;
    private Double balance;
    private Double internet;
    private Date date;

    --> //String of id's basically.
    private List<String> members;

    public PlanDetails(){}

    public PlanDetails(String id, String name, Double balance, Double internet, Date date, List<String> members){
        this.id = id;
        this.name = name;
        this.balance = balance;
        this.internet = internet;
        this.date = date;
        this.members = members;
    }

    public String getId() {
        return id;
    }

    /* Getter setters ommited for brevity */

Here is my handler class to handle my functional endpoints.

package com.startelco.plandetailsapi.handler;

import com.startelco.plandetailsapi.model.PlanDetails;
import com.startelco.plandetailsapi.repository.PlanRepository;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import static org.springframework.http.MediaType.APPLICATION_JSON;
@Component
public class PlanDetailsHandler {

    private PlanRepository repository;


    public PlanDetailsHandler(PlanRepository repository){
        this.repository = repository;
    }

    //Get All Users
    public Mono<ServerResponse> getAllUsers(ServerRequest request){
        Flux<PlanDetails> users = repository.findAll();

        return ServerResponse.ok()
                .contentType(APPLICATION_JSON)
                .body(users,PlanDetails.class);
    }

    --> //Get User by ID
    public Mono<ServerResponse> getUserDetails(ServerRequest request){
        String id = request.pathVariable("id");

        Mono<PlanDetails> userMono = repository.findById(id);
        Mono<ServerResponse> notFound = ServerResponse.notFound().build();

        return userMono.flatMap(user ->
                ServerResponse.ok()
                        .contentType(APPLICATION_JSON)
                        .body(BodyInserters.fromObject(user))
                        .switchIfEmpty(notFound)
                );
    }


    //Create a user
    public Mono<ServerResponse> saveUser(ServerRequest request){
        Mono<PlanDetails> userMono = request.bodyToMono(PlanDetails.class);

        return userMono.flatMap(user ->
                ServerResponse.status(HttpStatus.CREATED)
                        .contentType(APPLICATION_JSON)
                        .body(repository.save(user),PlanDetails.class)
                );
    }


    //Update user by ID
    public Mono<ServerResponse> updateUser(ServerRequest request) {
        String id = request.pathVariable("id");
        Mono<PlanDetails> existingUserMono = this.repository.findById(id);
        Mono<PlanDetails> userMono = request.bodyToMono(PlanDetails.class);

        Mono<ServerResponse> notFound = ServerResponse.notFound().build();

        return userMono.zipWith(existingUserMono,
                (user, existingUser) ->
                        new PlanDetails(existingUser.getId(), user.getName(), user.getBalance(), user.getInternet(),user.getDate(),user.getMembers())
        )
                .flatMap(user ->
                        ServerResponse.ok()
                                .contentType(APPLICATION_JSON)
                                .body(repository.save(user), PlanDetails.class)
                ).switchIfEmpty(notFound);
    }



    //Delete All Users
    public Mono<ServerResponse> deleteAllUsers(ServerRequest request) {
        return ServerResponse.ok()
                .build(repository.deleteAll());
    }
}

QUESTION: What is the Reactive way of coding getMemberDetails with reusing the previously written function getUserDetails.?

Requirement.

DESIRED FUNCTIONALITY : Get Member Details Function.

public Mono<ServerResponse> getMemberDetails(ServerRequest request) {
   --> //Pseudocode
   String id = request.pathVariable("id");

   Mono<PlanDetails> userMono = repository.findById(id);
   Mono<ServerResponse> notFound = ServerResponse.notFound().build();

   if(userMono.member is not null){
       int length = userMono.member.length
       Flux<PlanDetails> memberFlux;
       for(int i=0;i<length;i++){
            Mono<PlanDetails> member = getUserDetails(id=userMono.member[i]);
            memberFlux.add(member);
       }
    }else{
       return ServerResponse.ok().build(memberFlux=null or empty array);
    }

    return ServerResponse.ok().build(memberFlux.flatmap);

}

DESIRED BEHAVIOR

{
    "id": "5b42ecc11cde674475cab39a",
    "name": "Alex Svirsky",
    "balance": 140,
    "internet": 20,
    "date": 1531107095659,
    "members": [
        "5b42ecdd1cde674475cab39b",
        "5b42ed421cde674475cab39c",
        "5b42ed5d1cde674475cab39d"
    ]
}

REST call in my routes is defined as http://localhost:8080/users/members/5b42ecc11cde674475cab39a

[    {
        "id": "5b42ecdd1cde674475cab39b",
        "name": "Bob Marley",
        "balance": 120,
        "internet": 9,
        "date": 1531107095559,
        "members": null
    },
    {
        "id": "5b42ed421cde674475cab39c",
        "name": "Charlie Sheen",
        "balance": 10,
        "internet": 9,
        "date": 1531107095555,
        "members": null
    },
    {
        "id": "5b42ed5d1cde674475cab39d",
        "name": "Dale Carnegie",
        "balance": 100,
        "internet": 9,
        "date": 1531107055555,
        "members": null
    }
]

Upvotes: 5

Views: 11870

Answers (2)

technazi
technazi

Reputation: 958

I think this works for me finally. I am sorry if my pseudo code was not very clear. Thanks for your answer @Simon Baslé

    public Mono<ServerResponse> getMemberDetails(ServerRequest request) {
        String id = request.pathVariable("id");

        Mono<PlanDetails> userMono = repository.findById(id);
        Mono<ServerResponse> notFound = ServerResponse.notFound().build();
        Flux<PlanDetails> userFlux = userMono.flatMapIterable(user -> user.getMembers()).flatMap(repository::findById);

        return ServerResponse.ok()
                .contentType(APPLICATION_JSON)
                .body(userFlux,PlanDetails.class);
    }

Upvotes: 0

Simon Basl&#233;
Simon Basl&#233;

Reputation: 28301

In my opinion there isn't much that can be reused in that first method: as the two methods both return a fully-formed ServerResponse, you can't compose the originals. Which is merely repository.findById() with some boilerplate to transform that into an ok response or a 404 response...

So what you actually need is a way to compose getMemberDetails on top of many calls to repository.findById. If I interpret your pseudocode correctly, you want the response to just be a Flux<PlanDetail> of all the "members" under an original "user" (disregarding information about said original user)?

You should be able to do that reactively with a flatMap:

public Mono<ServerResponse> getMemberDetails(ServerRequest request) {
  String id = request.pathVariable("id");

   Mono<PlanDetails> userMono = repository.findById(id);
   Mono<ServerResponse> notFound = ServerResponse.notFound().build();

   return userMono
       //transform from user mono to Flux of member details
       .flatMap(user -> Flux.fromArray(user.member)) //if `member` is an Iterable use the following instead:
       //.flatMapIterable(user -> user.member)
       //now we have a Flux of member IDs, go get details
       .flatMap(repository::findById)
       //this will naturally ignore not found members
       //if no member is found or array of IDs is empty, the main sequence is itself empty at this point
       .switchIfEmpty(notFound);
}

Only caveat of this approach is that it doesn't distinguish between "not found" (the original user is not found) vs "no content" (the user has no members that can be found).

Upvotes: 8

Related Questions