How to create sub list in REST Reactive Spring

257 Views Asked by At

I am writing a test REST API application on Spring webFlux and R2DBC. The problem is that the list of articles, which is attached to the product can not be formed before sending it to the client.

Current response:

{
    "id": 1,
    "name": "test1",
    "description": "test description",
    "implement_cost": 200.0,
    "article_ids": {
        "scanAvailable": true,
        "prefetch": -1
    }
}

expected response:

{
    "id": 1,
    "name": "test1",
    "description": "test description",
    "implement_cost": 200.0,
    "article_ids": [
        1,
        2
    ]
}

How can I form sub-list for response?

Blocking operations are prohibited.

block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-tcp-nio-1 create array from flux

I have product with list of attached articles.

package ru.serov.distask.model;

import lombok.Data;
import reactor.core.publisher.Flux;
import ru.serov.distask.dao.repository.enity.ArticleEntity;

@Data
public class Product {
    private Long id;
    private String name;
    private String description;
    private Float implementCost;
    private Flux<ArticleEntity> articles;
}

public class ArticleEntity {
    @Id
    @Column(value = "id")
    private Long id;
    @Column(value = "product_id")
    private Long productId;
    @Column(value = "name")
    private String name;
    @Column(value = "content")
    private String content;
    @Column(value = "create_date")
    private LocalDate createDate;
}

To create a product, I make a separate request for articles to ReactiveCrudRepository:

package ru.serov.distask.dao.repository;

import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import ru.serov.distask.dao.repository.enity.ArticleEntity;

public interface IArticleEntityRepo extends ReactiveCrudRepository<ArticleEntity, Long> {

    @Query(value = "DELETE FROM company.articles WHERE product_id = $1")
    Mono<Void> deleteArticlesByProductId(Long productId);

    @Query(value = "SELECT * FROM company.articles WHERE  product_id = $1")
    Flux<ArticleEntity> findAllByProductId(Long productId);
}

after that I use the service to form the product. I use MapStruct as map auto-generator.

@Override
public Mono<Product> getProductById(Long id) {
    return productEntityService
            .getProductById(id)
            .flatMap(product -> articleEntityService
                    .getArticlesByProductId(id)
                    .collectList()
                    .map(articles -> mapper.entityToProduct(product, Flux.fromIterable(articles))));

}

Product RESTcontroller

@GetMapping("/{id}")
Mono<ArticleDTO> getArticleById(@PathVariable Long id) {
    return articleService
            .getArticleById(id)
            .flatMap(article -> Mono.just(articleMapper.entityToDTO(article)));
}

Mapper to ArticleDTO

@Mapper(componentModel = "spring")
public interface IProductDTOProductMapper {
    @Named("convertArticles")
    static Flux<Long> convertArticles(Flux<ArticleEntity> articles) {
        return articles.map(ArticleEntity::getId);
    }

    @Mapping(target = "implement_cost", source = "implementCost")
    @Mapping(target = "article_ids", source = "articles", qualifiedByName = "convertArticles")
    ProductDTO entityToDTO(Product src);
}
0

There are 0 best solutions below