Revert "Revert "Revert "BAEL-7540"""
This reverts commit fa1c5598b0429200709856c932301ae682a42678.
This commit is contained in:
		
							parent
							
								
									fa1c5598b0
								
							
						
					
					
						commit
						7c93d40044
					
				| @ -3,7 +3,6 @@ | ||||
| This module contains articles about Spring 5 WebFlux | ||||
| 
 | ||||
| ## Relevant articles: | ||||
| 
 | ||||
| - [Spring Webflux and @Cacheable Annotation](https://www.baeldung.com/spring-webflux-cacheable) | ||||
| - [Comparison Between Mono’s doOnNext() and doOnSuccess()](https://www.baeldung.com/mono-doonnext-doonsuccess) | ||||
| - [How to Access the First Element of a Flux](https://www.baeldung.com/java-flux-first-element) | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|          xmlns="http://maven.apache.org/POM/4.0.0" | ||||
|          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
| <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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||||
|     <modelVersion>4.0.0</modelVersion> | ||||
|     <artifactId>spring-5-webflux-2</artifactId> | ||||
|     <version>1.0-SNAPSHOT</version> | ||||
|  | ||||
| @ -41,6 +41,10 @@ public class Item { | ||||
| 
 | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         return "Item{" + "id='" + _id + '\'' + ", name='" + name + '\'' + ", price=" + price + '}'; | ||||
|         return "Item{" + | ||||
|                 "id='" + _id + '\'' + | ||||
|                 ", name='" + name + '\'' + | ||||
|                 ", price=" + price + | ||||
|                 '}'; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -16,8 +16,7 @@ public class ItemService { | ||||
| 
 | ||||
|     public ItemService(ItemRepository repository) { | ||||
|         this.repository = repository; | ||||
|         this.cache = Caffeine.newBuilder() | ||||
|             .build(this::getItem_withCaffeine); | ||||
|         this.cache = Caffeine.newBuilder().build(this::getItem_withCaffeine); | ||||
|     } | ||||
| 
 | ||||
|     @Cacheable("items") | ||||
| @ -31,14 +30,11 @@ public class ItemService { | ||||
| 
 | ||||
|     @Cacheable("items") | ||||
|     public Mono<Item> getItem_withCache(String id) { | ||||
|         return repository.findById(id) | ||||
|             .cache(); | ||||
|         return repository.findById(id).cache(); | ||||
|     } | ||||
| 
 | ||||
|     @Cacheable("items") | ||||
|     public Mono<Item> getItem_withCaffeine(String id) { | ||||
|         return cache.asMap() | ||||
|             .computeIfAbsent(id, k -> repository.findById(id) | ||||
|                 .cast(Item.class)); | ||||
|         return cache.asMap().computeIfAbsent(id, k -> repository.findById(id).cast(Item.class)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,35 +0,0 @@ | ||||
| package com.baeldung.webflux.exceptionhandeling.controller; | ||||
| 
 | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.web.bind.annotation.GetMapping; | ||||
| import org.springframework.web.bind.annotation.PathVariable; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
| 
 | ||||
| import com.baeldung.webflux.exceptionhandeling.ex.NotFoundException; | ||||
| import com.baeldung.webflux.exceptionhandeling.repository.UserRepository; | ||||
| import com.baeldung.webflux.zipwhen.model.User; | ||||
| 
 | ||||
| import reactor.core.publisher.Mono; | ||||
| 
 | ||||
| @RestController | ||||
| public class UserController { | ||||
| 
 | ||||
|     private final UserRepository userRepository; | ||||
| 
 | ||||
|     @Autowired | ||||
|     public UserController(UserRepository userRepository) { | ||||
|         this.userRepository = userRepository; | ||||
|     } | ||||
| 
 | ||||
|     @GetMapping("/user/{id}") | ||||
|     public Mono<User> getUserByIdThrowingException(@PathVariable String id) { | ||||
|         return userRepository.findById(id) | ||||
|           .switchIfEmpty(Mono.error(new NotFoundException("User not found"))); | ||||
|     } | ||||
| 
 | ||||
|     @GetMapping("/user/{id}") | ||||
|     public Mono<User> getUserByIdUsingMonoError(@PathVariable String id) { | ||||
|         return userRepository.findById(id) | ||||
|           .switchIfEmpty(Mono.error(() -> new NotFoundException("User not found"))); | ||||
|     } | ||||
| } | ||||
| @ -1,12 +0,0 @@ | ||||
| package com.baeldung.webflux.exceptionhandeling.ex; | ||||
| 
 | ||||
| public class NotFoundException extends RuntimeException { | ||||
| 
 | ||||
|     public NotFoundException(String message) { | ||||
|         super(message); | ||||
|     } | ||||
| 
 | ||||
|     public NotFoundException(String message, Throwable cause) { | ||||
|         super(message, cause); | ||||
|     } | ||||
| } | ||||
| @ -1,29 +0,0 @@ | ||||
| package com.baeldung.webflux.exceptionhandeling.model; | ||||
| 
 | ||||
| public class User { | ||||
|     private String id; | ||||
|     private String username; | ||||
| 
 | ||||
|     public String getId() { | ||||
|         return id; | ||||
|     } | ||||
| 
 | ||||
|     public void setId(String id) { | ||||
|         this.id = id; | ||||
|     } | ||||
| 
 | ||||
|     public String getUsername() { | ||||
|         return username; | ||||
|     } | ||||
| 
 | ||||
|     public void setUsername(String username) { | ||||
|         this.username = username; | ||||
|     } | ||||
| 
 | ||||
|     public User(String userId, String userName) { | ||||
|         this.id=userId; | ||||
|         this.username=userName; | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -1,24 +0,0 @@ | ||||
| package com.baeldung.webflux.exceptionhandeling.repository; | ||||
| 
 | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.ConcurrentHashMap; | ||||
| 
 | ||||
| import org.springframework.stereotype.Repository; | ||||
| 
 | ||||
| import com.baeldung.webflux.zipwhen.model.User; | ||||
| 
 | ||||
| import reactor.core.publisher.Mono; | ||||
| 
 | ||||
| @Repository | ||||
| public class UserRepository { | ||||
|     private final Map<String, User> userDatabase = new ConcurrentHashMap<>(); | ||||
| 
 | ||||
|     public UserRepository() { | ||||
|         userDatabase.put("1", new User("1", "John Doe")); | ||||
|         userDatabase.put("2", new User("2", "Jane Smith")); | ||||
|     } | ||||
| 
 | ||||
|     public Mono<User> findById(String id) { | ||||
|         return Mono.justOrEmpty(userDatabase.get(id)); | ||||
|     } | ||||
| } | ||||
| @ -1,9 +1,9 @@ | ||||
| package com.baeldung.webflux.filerecord; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import org.springframework.data.annotation.Id; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| public class FileRecord { | ||||
|     @Id | ||||
|     private int id; | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| package com.baeldung.webflux.filerecord; | ||||
| 
 | ||||
| import io.r2dbc.spi.ConnectionFactory; | ||||
| import org.springframework.boot.SpringApplication; | ||||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||||
| import org.springframework.context.annotation.Bean; | ||||
| @ -7,8 +8,6 @@ import org.springframework.core.io.ClassPathResource; | ||||
| import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer; | ||||
| import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator; | ||||
| 
 | ||||
| import io.r2dbc.spi.ConnectionFactory; | ||||
| 
 | ||||
| @SpringBootApplication | ||||
| public class FileRecordApplication { | ||||
| 
 | ||||
|  | ||||
| @ -1,13 +1,12 @@ | ||||
| package com.baeldung.webflux.filerecord; | ||||
| 
 | ||||
| import java.nio.file.Paths; | ||||
| 
 | ||||
| import org.springframework.http.codec.multipart.FilePart; | ||||
| import org.springframework.web.bind.annotation.*; | ||||
| 
 | ||||
| import reactor.core.publisher.Flux; | ||||
| import reactor.core.publisher.Mono; | ||||
| 
 | ||||
| import java.nio.file.Paths; | ||||
| 
 | ||||
| @RestController | ||||
| public class FileRecordController { | ||||
| 
 | ||||
| @ -29,7 +28,7 @@ public class FileRecordController { | ||||
|         FileRecord fileRecord = new FileRecord(); | ||||
| 
 | ||||
|         return filePartFlux.flatMap(filePart -> filePart.transferTo(Paths.get(filePart.filename())) | ||||
|                 .then(Mono.just(filePart.filename()))) | ||||
|             .then(Mono.just(filePart.filename()))) | ||||
|             .collectList() | ||||
|             .flatMap(filenames -> { | ||||
|                 fileRecord.setFilenames(filenames); | ||||
|  | ||||
| @ -1,7 +1,6 @@ | ||||
| package com.baeldung.webflux.filerecord; | ||||
| 
 | ||||
| import org.springframework.stereotype.Service; | ||||
| 
 | ||||
| import reactor.core.publisher.Mono; | ||||
| 
 | ||||
| @Service | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| package com.baeldung.webflux.model; | ||||
| 
 | ||||
| 
 | ||||
| public class Payment { | ||||
|     private final int amount; | ||||
| 
 | ||||
|  | ||||
| @ -11,10 +11,10 @@ public class EmailService { | ||||
| 
 | ||||
|     public Mono<Boolean> sendEmail(String userId) { | ||||
|         return userService.getUser(userId) | ||||
|             .flatMap(user -> { | ||||
|                 System.out.println("Sending email to: " + user.getEmail()); | ||||
|                 return Mono.just(true); | ||||
|             }) | ||||
|             .defaultIfEmpty(false); | ||||
|           .flatMap(user -> { | ||||
|               System.out.println("Sending email to: " + user.getEmail()); | ||||
|               return Mono.just(true); | ||||
|           }) | ||||
|           .defaultIfEmpty(false); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -28,16 +28,16 @@ public class UserController { | ||||
|     public Mono<ResponseEntity<String>> combineAllDataFor(@PathVariable String userId) { | ||||
|         Mono<User> userMono = userService.getUser(userId); | ||||
|         Mono<Boolean> emailSentMono = emailService.sendEmail(userId) | ||||
|             .subscribeOn(Schedulers.parallel()); | ||||
|           .subscribeOn(Schedulers.parallel()); | ||||
|         Mono<String> databaseResultMono = userMono.flatMap(user -> databaseService.saveUserData(user) | ||||
|             .map(Object::toString)); | ||||
|           .map(Object::toString)); | ||||
| 
 | ||||
|         return userMono.zipWhen(user -> emailSentMono, Tuples::of) | ||||
|             .zipWhen(tuple -> databaseResultMono, (tuple, databaseResult) -> { | ||||
|                 User user = tuple.getT1(); | ||||
|                 Boolean emailSent = tuple.getT2(); | ||||
|                 return ResponseEntity.ok() | ||||
|                     .body("Response: " + user + ", Email Sent: " + emailSent + ", Database Result: " + databaseResult); | ||||
|             }); | ||||
|           .zipWhen(tuple -> databaseResultMono, (tuple, databaseResult) -> { | ||||
|               User user = tuple.getT1(); | ||||
|               Boolean emailSent = tuple.getT2(); | ||||
|               return ResponseEntity.ok() | ||||
|                 .body("Response: " + user + ", Email Sent: " + emailSent + ", Database Result: " + databaseResult); | ||||
|           }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <configuration> | ||||
|     <appender name="Console" | ||||
|               class="ch.qos.logback.core.ConsoleAppender"> | ||||
|         class="ch.qos.logback.core.ConsoleAppender"> | ||||
|         <layout class="ch.qos.logback.classic.PatternLayout"> | ||||
|             <Pattern> | ||||
|                 %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable | ||||
|  | ||||
| @ -1,6 +1,5 @@ | ||||
| package com.baeldung.webflux.caching; | ||||
| 
 | ||||
| import static org.assertj.core.api.Assertions.assertThat; | ||||
| 
 | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| @ -13,10 +12,13 @@ import org.testcontainers.utility.DockerImageName; | ||||
| 
 | ||||
| import reactor.core.publisher.Mono; | ||||
| 
 | ||||
| import static org.assertj.core.api.Assertions.assertThat; | ||||
| 
 | ||||
| @SpringBootTest | ||||
| @ActiveProfiles("cache") | ||||
| public class MonoFluxResultCachingLiveTest { | ||||
| 
 | ||||
| 
 | ||||
|     @Autowired | ||||
|     ItemService itemService; | ||||
| 
 | ||||
| @ -28,34 +30,32 @@ public class MonoFluxResultCachingLiveTest { | ||||
|         registry.add("spring.data.mongodb.uri", mongoDBContainer::getReplicaSetUrl); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void givenItem_whenGetItemIsCalled_thenMonoIsCached() { | ||||
|         Mono<Item> glass = itemService.save(new Item("glass", 1.00)); | ||||
| @Test | ||||
| public void givenItem_whenGetItemIsCalled_thenMonoIsCached() { | ||||
|     Mono<Item> glass = itemService.save(new Item("glass", 1.00)); | ||||
| 
 | ||||
|         String id = glass.block() | ||||
|             .get_id(); | ||||
|     String id = glass.block().get_id(); | ||||
| 
 | ||||
|         Mono<Item> mono = itemService.getItem(id); | ||||
|         Item item = mono.block(); | ||||
|     Mono<Item> mono = itemService.getItem(id); | ||||
|     Item item = mono.block(); | ||||
| 
 | ||||
|         assertThat(item).isNotNull(); | ||||
|         assertThat(item.getName()).isEqualTo("glass"); | ||||
|         assertThat(item.getPrice()).isEqualTo(1.00); | ||||
|     assertThat(item).isNotNull(); | ||||
|     assertThat(item.getName()).isEqualTo("glass"); | ||||
|     assertThat(item.getPrice()).isEqualTo(1.00); | ||||
| 
 | ||||
|         Mono<Item> mono2 = itemService.getItem(id); | ||||
|         Item item2 = mono2.block(); | ||||
|     Mono<Item> mono2 = itemService.getItem(id); | ||||
|     Item item2 = mono2.block(); | ||||
| 
 | ||||
|         assertThat(item2).isNotNull(); | ||||
|         assertThat(item2.getName()).isEqualTo("glass"); | ||||
|         assertThat(item2.getPrice()).isEqualTo(1.00); | ||||
|     } | ||||
|     assertThat(item2).isNotNull(); | ||||
|     assertThat(item2.getName()).isEqualTo("glass"); | ||||
|     assertThat(item2.getPrice()).isEqualTo(1.00); | ||||
| } | ||||
| 
 | ||||
|     @Test | ||||
|     public void givenItem_whenGetItemWithCacheIsCalled_thenMonoResultIsCached() { | ||||
|         Mono<Item> glass = itemService.save(new Item("glass", 1.00)); | ||||
| 
 | ||||
|         String id = glass.block() | ||||
|             .get_id(); | ||||
|         String id = glass.block().get_id(); | ||||
| 
 | ||||
|         Mono<Item> mono = itemService.getItem_withCache(id); | ||||
|         Item item = mono.block(); | ||||
| @ -76,8 +76,7 @@ public class MonoFluxResultCachingLiveTest { | ||||
|     public void givenItem_whenGetItemWithCaffeineIsCalled_thenMonoResultIsCached() { | ||||
|         Mono<Item> glass = itemService.save(new Item("glass", 1.00)); | ||||
| 
 | ||||
|         String id = glass.block() | ||||
|             .get_id(); | ||||
|         String id = glass.block().get_id(); | ||||
| 
 | ||||
|         Mono<Item> mono = itemService.getItem_withCaffeine(id); | ||||
|         Item item = mono.block(); | ||||
|  | ||||
| @ -1,48 +0,0 @@ | ||||
| package com.baeldung.webflux.exceptionhandeling; | ||||
| 
 | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.mockito.InjectMocks; | ||||
| import org.mockito.Mock; | ||||
| import org.mockito.MockitoAnnotations; | ||||
| 
 | ||||
| import reactor.core.publisher.Mono; | ||||
| import reactor.test.StepVerifier; | ||||
| 
 | ||||
| import static org.junit.jupiter.api.Assertions.assertThrows; | ||||
| 
 | ||||
| import static org.mockito.Mockito.when; | ||||
| 
 | ||||
| import com.baeldung.webflux.exceptionhandeling.controller.UserController; | ||||
| import com.baeldung.webflux.exceptionhandeling.ex.NotFoundException; | ||||
| import com.baeldung.webflux.exceptionhandeling.repository.UserRepository; | ||||
| 
 | ||||
| public class UserControllerUnitTest { | ||||
|     @Mock | ||||
|     private UserRepository userRepository; | ||||
| 
 | ||||
|     @InjectMocks | ||||
|     private UserController userController; | ||||
| 
 | ||||
|     public UserControllerUnitTest() { | ||||
|         MockitoAnnotations.initMocks(this); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetUserByIdUsingMonoError_UserNotFound() { | ||||
|         String userId = "3"; | ||||
|         when(userRepository.findById(userId)).thenReturn(Mono.empty()); | ||||
|         StepVerifier.create(userController.getUserByIdUsingMonoError(userId)) | ||||
|           .expectError(NotFoundException.class) | ||||
|           .verify(); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void testGetUserByIdThrowingException_UserNotFound() { | ||||
|         String userId = "3"; | ||||
|         when(userRepository.findById(userId)).thenReturn(Mono.empty()); | ||||
|         assertThrows(NotFoundException.class, () -> userController.getUserByIdThrowingException(userId) | ||||
|           .block()); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @ -1,10 +1,5 @@ | ||||
| package com.baeldung.webflux.filerecord; | ||||
| 
 | ||||
| import static org.mockito.ArgumentMatchers.any; | ||||
| import static org.mockito.Mockito.when; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import org.junit.jupiter.api.Test; | ||||
| import org.junit.jupiter.api.extension.ExtendWith; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| @ -18,9 +13,13 @@ import org.springframework.test.context.ContextConfiguration; | ||||
| import org.springframework.test.context.junit.jupiter.SpringExtension; | ||||
| import org.springframework.test.web.reactive.server.WebTestClient; | ||||
| import org.springframework.util.LinkedMultiValueMap; | ||||
| 
 | ||||
| import reactor.core.publisher.Mono; | ||||
| 
 | ||||
| import java.util.List; | ||||
| 
 | ||||
| import static org.mockito.ArgumentMatchers.any; | ||||
| import static org.mockito.Mockito.when; | ||||
| 
 | ||||
| @ExtendWith(SpringExtension.class) | ||||
| @SpringBootTest(classes = FileRecordController.class) | ||||
| @ContextConfiguration(classes = { FileRecordController.class }) | ||||
|  | ||||
| @ -7,7 +7,6 @@ import java.util.Optional; | ||||
| import org.junit.jupiter.api.Test; | ||||
| 
 | ||||
| import com.baeldung.webflux.model.Payment; | ||||
| 
 | ||||
| import reactor.core.publisher.Flux; | ||||
| import reactor.core.publisher.Mono; | ||||
| import reactor.test.StepVerifier; | ||||
|  | ||||
| @ -25,19 +25,19 @@ public class UserControllerUnitTest { | ||||
|         User user = new User(userId, "John Doe"); | ||||
| 
 | ||||
|         Mockito.when(userService.getUser(userId)) | ||||
|             .thenReturn(Mono.just(user)); | ||||
|           .thenReturn(Mono.just(user)); | ||||
|         Mockito.when(emailService.sendEmail(userId)) | ||||
|             .thenReturn(Mono.just(true)); | ||||
|           .thenReturn(Mono.just(true)); | ||||
|         Mockito.when(databaseService.saveUserData(user)) | ||||
|             .thenReturn(Mono.just(true)); | ||||
|           .thenReturn(Mono.just(true)); | ||||
| 
 | ||||
|         UserController userController = new UserController(userService, emailService, databaseService); | ||||
| 
 | ||||
|         Mono<ResponseEntity<String>> responseMono = userController.combineAllDataFor(userId); | ||||
| 
 | ||||
|         StepVerifier.create(responseMono) | ||||
|             .expectNextMatches(responseEntity -> responseEntity.getStatusCode() == HttpStatus.OK && responseEntity.getBody() | ||||
|                 .equals("Response: " + user + ", Email Sent: true, Database Result: " + true)) | ||||
|             .verifyComplete(); | ||||
|           .expectNextMatches(responseEntity -> responseEntity.getStatusCode() == HttpStatus.OK && responseEntity.getBody() | ||||
|             .equals("Response: " + user + ", Email Sent: true, Database Result: " + true)) | ||||
|           .verifyComplete(); | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user