Commit 03cae237 authored by Perwaiz Ali's avatar Perwaiz Ali

Reactive CURD and Test cases

parents
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="LOCAL" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="D:/Gradle/gradle-7.5.1" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
<option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="4f6f6b6d-a217-448a-b0e8-aed55a32d6a1" name="Default Changelist" comment="" />
<ignored path="$PROJECT_DIR$/out/" />
<ignored path="$PROJECT_DIR$/.gradle/" />
<ignored path="$PROJECT_DIR$/build/" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="DefaultGradleProjectSettings">
<option name="testRunner" value="GRADLE" />
<option name="delegatedBuild" value="true" />
</component>
<component name="ExternalProjectsData">
<projectState path="$PROJECT_DIR$">
<ProjectState />
</projectState>
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/spring-reactive-mongo-crud-main/src/main/java/com/javatechie/reactive/SpringReactiveMongoCrudApplication.java">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="204">
<caret line="13" selection-start-line="13" selection-end-line="13" />
<folding>
<element signature="e#269#270#0" expanded="true" />
<element signature="e#345#346#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/spring-reactive-mongo-crud-main/src/main/java/com/javatechie/reactive/utils/AppUtils.java">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</file>
</leaf>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="85" />
<option name="y" value="25" />
<option name="width" value="1750" />
<option name="height" value="980" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="PackagesPane" />
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="spring-reactive-mongo-crud-main" type="b2602c69:ProjectViewProjectNode" />
<item name="spring-reactive-mongo-crud-main" type="8a07ba80:GradleTreeStructureProvider$GradleModuleDirectoryNode" />
</path>
<path>
<item name="spring-reactive-mongo-crud-main" type="b2602c69:ProjectViewProjectNode" />
<item name="spring-reactive-mongo-crud-main" type="8a07ba80:GradleTreeStructureProvider$GradleModuleDirectoryNode" />
<item name="spring-reactive-mongo-crud-main" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="spring-reactive-mongo-crud-main" type="b2602c69:ProjectViewProjectNode" />
<item name="spring-reactive-mongo-crud-main" type="8a07ba80:GradleTreeStructureProvider$GradleModuleDirectoryNode" />
<item name="spring-reactive-mongo-crud-main" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="spring-reactive-mongo-crud-main" type="b2602c69:ProjectViewProjectNode" />
<item name="spring-reactive-mongo-crud-main" type="8a07ba80:GradleTreeStructureProvider$GradleModuleDirectoryNode" />
<item name="spring-reactive-mongo-crud-main" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="main" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="spring-reactive-mongo-crud-main" type="b2602c69:ProjectViewProjectNode" />
<item name="spring-reactive-mongo-crud-main" type="8a07ba80:GradleTreeStructureProvider$GradleModuleDirectoryNode" />
<item name="spring-reactive-mongo-crud-main" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="main" type="462c0819:PsiDirectoryNode" />
<item name="java" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="spring-reactive-mongo-crud-main" type="b2602c69:ProjectViewProjectNode" />
<item name="spring-reactive-mongo-crud-main" type="8a07ba80:GradleTreeStructureProvider$GradleModuleDirectoryNode" />
<item name="spring-reactive-mongo-crud-main" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="main" type="462c0819:PsiDirectoryNode" />
<item name="java" type="462c0819:PsiDirectoryNode" />
<item name="com" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="spring-reactive-mongo-crud-main" type="b2602c69:ProjectViewProjectNode" />
<item name="spring-reactive-mongo-crud-main" type="8a07ba80:GradleTreeStructureProvider$GradleModuleDirectoryNode" />
<item name="spring-reactive-mongo-crud-main" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="main" type="462c0819:PsiDirectoryNode" />
<item name="java" type="462c0819:PsiDirectoryNode" />
<item name="com" type="462c0819:PsiDirectoryNode" />
<item name="javatechie" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="spring-reactive-mongo-crud-main" type="b2602c69:ProjectViewProjectNode" />
<item name="spring-reactive-mongo-crud-main" type="8a07ba80:GradleTreeStructureProvider$GradleModuleDirectoryNode" />
<item name="spring-reactive-mongo-crud-main" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="main" type="462c0819:PsiDirectoryNode" />
<item name="java" type="462c0819:PsiDirectoryNode" />
<item name="com" type="462c0819:PsiDirectoryNode" />
<item name="javatechie" type="462c0819:PsiDirectoryNode" />
<item name="reactive" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="4f6f6b6d-a217-448a-b0e8-aed55a32d6a1" name="Default Changelist" comment="" />
<created>1680117476518</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1680117476518</updated>
</task>
<servers />
</component>
<component name="ToolWindowManager">
<frame x="-7" y="-7" width="1550" height="838" extended-state="6" />
<layout>
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.25302014" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info id="Image Layers" order="2" />
<window_info id="Designer" order="3" />
<window_info id="Capture Tool" order="4" />
<window_info id="UI Designer" order="5" />
<window_info id="Favorites" order="6" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info anchor="bottom" id="Run" order="2" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="bottom" id="Terminal" order="7" />
<window_info anchor="bottom" id="Event Log" order="8" side_tool="true" />
<window_info anchor="bottom" id="Version Control" order="9" />
<window_info anchor="bottom" id="Build" order="10" visible="true" weight="0.3286119" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
<window_info anchor="right" id="Palette" order="3" />
<window_info anchor="right" id="Maven" order="4" />
<window_info anchor="right" id="Gradle" order="5" />
<window_info anchor="right" id="Theme Preview" order="6" />
<window_info anchor="right" id="Capture Analysis" order="7" />
<window_info anchor="right" id="Palette&#9;" order="8" />
</layout>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/spring-reactive-mongo-crud-main/src/main/java/com/javatechie/reactive/SpringReactiveMongoCrudApplication.java">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="204">
<caret line="13" selection-start-line="13" selection-end-line="13" />
<folding>
<element signature="e#269#270#0" expanded="true" />
<element signature="e#345#346#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/spring-reactive-mongo-crud-main/src/main/java/com/javatechie/reactive/utils/AppUtils.java">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</component>
<component name="masterDetails">
<states>
<state key="ProjectJDKs.UI">
<settings>
<last-edited>11</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.javatechie</groupId>
<artifactId>spring-reactive-mongo-crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-reactive-mongo-crud</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.javatechie.reactive;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringReactiveMongoCrudApplication {
public static void main(String[] args) {
SpringApplication.run(SpringReactiveMongoCrudApplication.class, args);
}
}
package com.javatechie.reactive.controller;
import com.javatechie.reactive.dto.ProductDto;
import com.javatechie.reactive.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@RestController
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductService service;
@GetMapping
public Flux<ProductDto> getProducts(){
return service.getProducts();
}
@GetMapping("/{id}")
public Mono<ProductDto> getProduct(@PathVariable String id){
return service.getProduct(id);
}
@GetMapping("/product-range")
public Flux<ProductDto> getProductBetweenRange(@RequestParam("min") double min, @RequestParam("max")double max){
return service.getProductInRange(min,max);
}
@PostMapping
public Mono<ProductDto> saveProduct(@RequestBody Mono<ProductDto> productDtoMono){
System.out.println("controller method called ...");
return service.saveProduct(productDtoMono);
}
@PutMapping("/update/{id}")
public Mono<ProductDto> updateProduct(@RequestBody Mono<ProductDto> productDtoMono,@PathVariable String id){
return service.updateProduct(productDtoMono,id);
}
@DeleteMapping("/delete/{id}")
public Mono<Void> deleteProduct(@PathVariable String id){
return service.deleteProduct(id);
}
}
package com.javatechie.reactive.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductDto {
private String id;
private String name;
private int qty;
private double price;
}
package com.javatechie.reactive.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(collection = "products")
public class Product {
@Id
private String id;
private String name;
private int qty;
private double price;
}
package com.javatechie.reactive.repository;
import com.javatechie.reactive.dto.ProductDto;
import com.javatechie.reactive.entity.Product;
import org.springframework.data.domain.Range;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
@Repository
public interface ProductRepository extends ReactiveMongoRepository<Product,String> {
Flux<ProductDto> findByPriceBetween(Range<Double> priceRange);
}
package com.javatechie.reactive.service;
import com.javatechie.reactive.dto.ProductDto;
import com.javatechie.reactive.entity.Product;
import com.javatechie.reactive.repository.ProductRepository;
import com.javatechie.reactive.utils.AppUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Range;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
public class ProductService {
@Autowired
private ProductRepository repository;
public Flux<ProductDto> getProducts(){
return repository.findAll().map(AppUtils::entityToDto);
}
public Mono<ProductDto> getProduct(String id){
return repository.findById(id).map(AppUtils::entityToDto);
}
public Flux<ProductDto> getProductInRange(double min,double max){
return repository.findByPriceBetween(Range.closed(min,max));
}
public Mono<ProductDto> saveProduct(Mono<ProductDto> productDtoMono){
System.out.println("service method called ...");
return productDtoMono.map(AppUtils::dtoToEntity)
.flatMap(repository::insert)
.map(AppUtils::entityToDto);
}
public Mono<ProductDto> updateProduct(Mono<ProductDto> productDtoMono,String id){
return repository.findById(id)
.flatMap(p->productDtoMono.map(AppUtils::dtoToEntity)
.doOnNext(e->e.setId(id)))
.flatMap(repository::save)
.map(AppUtils::entityToDto);
}
public Mono<Void> deleteProduct(String id){
return repository.deleteById(id);
}
}
package com.javatechie.reactive.utils;
import com.javatechie.reactive.dto.ProductDto;
import com.javatechie.reactive.entity.Product;
import org.springframework.beans.BeanUtils;
public class AppUtils {
public static ProductDto entityToDto(Product product) {
ProductDto productDto = new ProductDto();
BeanUtils.copyProperties(product, productDto);
return productDto;
}
public static Product dtoToEntity(ProductDto productDto) {
Product product = new Product();
BeanUtils.copyProperties(productDto, product);
return product;
}
}
spring:
data:
mongodb:
database: Productsdb
host: localhost
port: 27017
server:
port: 9292
\ No newline at end of file
package com.javatechie.reactive;
import com.javatechie.reactive.controller.ProductController;
import com.javatechie.reactive.dto.ProductDto;
import com.javatechie.reactive.service.ProductService;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.when;
@RunWith(SpringRunner.class)
@WebFluxTest(ProductController.class)
class SpringReactiveMongoCrudApplicationTests {
@Autowired
private WebTestClient webTestClient;
@MockBean
private ProductService service;
@Test
public void addProductTest(){
Mono<ProductDto> productDtoMono=Mono.just(new ProductDto("102","mobile",1,10000));
when(service.saveProduct(productDtoMono)).thenReturn(productDtoMono);
webTestClient.post().uri("/products")
.body(Mono.just(productDtoMono),ProductDto.class)
.exchange()
.expectStatus().isOk();//200
}
@Test
public void getProductsTest(){
Flux<ProductDto> productDtoFlux=Flux.just(new ProductDto("102","mobile",1,10000),
new ProductDto("103","TV",1,50000));
when(service.getProducts()).thenReturn(productDtoFlux);
Flux<ProductDto> responseBody = webTestClient.get().uri("/products")
.exchange()
.expectStatus().isOk()
.returnResult(ProductDto.class)
.getResponseBody();
StepVerifier.create(responseBody)
.expectSubscription()
.expectNext(new ProductDto("102","mobile",1,10000))
.expectNext(new ProductDto("103","TV",1,50000))
.verifyComplete();
}
@Test
public void getProductTest(){
Mono<ProductDto> productDtoMono=Mono.just(new ProductDto("102","mobile",1,10000));
when(service.getProduct(any())).thenReturn(productDtoMono);
Flux<ProductDto> responseBody = webTestClient.get().uri("/products/102")
.exchange()
.expectStatus().isOk()
.returnResult(ProductDto.class)
.getResponseBody();
StepVerifier.create(responseBody)
.expectSubscription()
.expectNextMatches(p->p.getName().equals("mobile"))
.verifyComplete();
}
@Test
public void updateProductTest(){
Mono<ProductDto> productDtoMono=Mono.just(new ProductDto("102","mobile",1,10000));
when(service.updateProduct(productDtoMono,"102")).thenReturn(productDtoMono);
webTestClient.put().uri("/products/update/102")
.body(Mono.just(productDtoMono),ProductDto.class)
.exchange()
.expectStatus().isOk();//200
}
@Test
public void deleteProductTest(){
given(service.deleteProduct(any())).willReturn(Mono.empty());
webTestClient.delete().uri("/products/delete/102")
.exchange()
.expectStatus().isOk();//200
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment