Merge pull request #12137 from hkhan/JAVA-8148-update-guide-webflux

Java 8148 update guide webflux
This commit is contained in:
kwoyke 2022-05-02 11:09:37 +02:00 committed by GitHub
commit 64999b737c
8 changed files with 151 additions and 147 deletions

View File

@ -11,7 +11,5 @@ public class Employee {
private String id; private String id;
private String name; private String name;
// standard getters and setters
} }

View File

@ -10,46 +10,32 @@ import java.util.Map;
@Repository @Repository
public class EmployeeRepository { public class EmployeeRepository {
static Map<String, Employee> employeeData; private static final Map<String, Employee> EMPLOYEE_DATA;
static Map<String, String> employeeAccessData;
static { static {
employeeData = new HashMap<>(); EMPLOYEE_DATA = new HashMap<>();
employeeData.put("1", new Employee("1", "Employee 1")); EMPLOYEE_DATA.put("1", new Employee("1", "Employee 1"));
employeeData.put("2", new Employee("2", "Employee 2")); EMPLOYEE_DATA.put("2", new Employee("2", "Employee 2"));
employeeData.put("3", new Employee("3", "Employee 3")); EMPLOYEE_DATA.put("3", new Employee("3", "Employee 3"));
employeeData.put("4", new Employee("4", "Employee 4")); EMPLOYEE_DATA.put("4", new Employee("4", "Employee 4"));
employeeData.put("5", new Employee("5", "Employee 5")); EMPLOYEE_DATA.put("5", new Employee("5", "Employee 5"));
employeeData.put("6", new Employee("6", "Employee 6")); EMPLOYEE_DATA.put("6", new Employee("6", "Employee 6"));
employeeData.put("7", new Employee("7", "Employee 7")); EMPLOYEE_DATA.put("7", new Employee("7", "Employee 7"));
employeeData.put("8", new Employee("8", "Employee 8")); EMPLOYEE_DATA.put("8", new Employee("8", "Employee 8"));
employeeData.put("9", new Employee("9", "Employee 9")); EMPLOYEE_DATA.put("9", new Employee("9", "Employee 9"));
employeeData.put("10", new Employee("10", "Employee 10")); EMPLOYEE_DATA.put("10", new Employee("10", "Employee 10"));
employeeAccessData = new HashMap<>();
employeeAccessData.put("1", "Employee 1 Access Key");
employeeAccessData.put("2", "Employee 2 Access Key");
employeeAccessData.put("3", "Employee 3 Access Key");
employeeAccessData.put("4", "Employee 4 Access Key");
employeeAccessData.put("5", "Employee 5 Access Key");
employeeAccessData.put("6", "Employee 6 Access Key");
employeeAccessData.put("7", "Employee 7 Access Key");
employeeAccessData.put("8", "Employee 8 Access Key");
employeeAccessData.put("9", "Employee 9 Access Key");
employeeAccessData.put("10", "Employee 10 Access Key");
} }
public Mono<Employee> findEmployeeById(String id) { public Mono<Employee> findEmployeeById(String id) {
return Mono.just(employeeData.get(id)); return Mono.just(EMPLOYEE_DATA.get(id));
} }
public Flux<Employee> findAllEmployees() { public Flux<Employee> findAllEmployees() {
return Flux.fromIterable(employeeData.values()); return Flux.fromIterable(EMPLOYEE_DATA.values());
} }
public Mono<Employee> updateEmployee(Employee employee) { public Mono<Employee> updateEmployee(Employee employee) {
Employee existingEmployee = employeeData.get(employee.getId()); Employee existingEmployee = EMPLOYEE_DATA.get(employee.getId());
if (existingEmployee != null) { if (existingEmployee != null) {
existingEmployee.setName(employee.getName()); existingEmployee.setName(employee.getName());
} }

View File

@ -1,12 +1,19 @@
package com.baeldung.reactive.webflux.annotation; package com.baeldung.reactive.webflux.annotation;
import com.baeldung.reactive.webflux.EmployeeRepository;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration;
import org.springframework.context.annotation.Bean;
@SpringBootApplication(exclude = MongoReactiveAutoConfiguration.class) @SpringBootApplication(exclude = MongoReactiveAutoConfiguration.class)
public class EmployeeSpringApplication { public class EmployeeSpringApplication {
@Bean
EmployeeRepository employeeRepository() {
return new EmployeeRepository();
}
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(EmployeeSpringApplication.class, args); SpringApplication.run(EmployeeSpringApplication.class, args);

View File

@ -10,23 +10,23 @@ import reactor.core.publisher.Mono;
public class EmployeeWebClient { public class EmployeeWebClient {
private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeWebClient.class); private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeWebClient.class);
WebClient client = WebClient.create("http://localhost:8080"); WebClient client = WebClient.create("http://localhost:8080");
public void consume() { public void consume() {
Mono<Employee> employeeMono = client.get() Mono<Employee> employeeMono = client.get()
.uri("/employees/{id}", "1") .uri("/employees/{id}", "1")
.retrieve() .retrieve()
.bodyToMono(Employee.class); .bodyToMono(Employee.class);
employeeMono.subscribe(employee -> LOGGER.info("Employee: {}", employee));
employeeMono.subscribe(employee -> LOGGER.debug("Employee: {}", employee));
Flux<Employee> employeeFlux = client.get() Flux<Employee> employeeFlux = client.get()
.uri("/employees") .uri("/employees")
.retrieve() .retrieve()
.bodyToFlux(Employee.class); .bodyToFlux(Employee.class);
employeeFlux.subscribe(employee -> LOGGER.debug("Employee: {}", employee)); employeeFlux.subscribe(employee -> LOGGER.info("Employee: {}", employee));
} }
} }

View File

@ -13,33 +13,31 @@ import org.springframework.security.web.server.SecurityWebFilterChain;
@EnableWebFluxSecurity @EnableWebFluxSecurity
public class EmployeeWebSecurityConfig { public class EmployeeWebSecurityConfig {
@Bean @Bean
public MapReactiveUserDetailsService userDetailsService() { public MapReactiveUserDetailsService userDetailsService() {
UserDetails user = User UserDetails user = User
.withUsername("admin") .withUsername("admin")
.password(passwordEncoder().encode("password")) .password(passwordEncoder().encode("password"))
.roles("ADMIN") .roles("ADMIN")
.build(); .build();
return new MapReactiveUserDetailsService(user); return new MapReactiveUserDetailsService(user);
} }
@Bean @Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.csrf() http
.disable() .csrf().disable()
.authorizeExchange() .authorizeExchange()
.pathMatchers(HttpMethod.POST, "/employees/update") .pathMatchers(HttpMethod.POST, "/employees/update").hasRole("ADMIN")
.hasRole("ADMIN") .pathMatchers("/**").permitAll()
.pathMatchers("/**") .and()
.permitAll() .httpBasic();
.and()
.httpBasic();
return http.build(); return http.build();
} }
@Bean @Bean
public PasswordEncoder passwordEncoder() { public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); return new BCryptPasswordEncoder();
} }
} }

View File

@ -25,50 +25,49 @@ public class EmployeeFunctionalConfig {
@Bean @Bean
RouterFunction<ServerResponse> getAllEmployeesRoute() { RouterFunction<ServerResponse> getAllEmployeesRoute() {
return route(GET("/employees"), return route(GET("/employees"),
req -> ok().body( req -> ok().body(
employeeRepository().findAllEmployees(), Employee.class)); employeeRepository().findAllEmployees(), Employee.class));
} }
@Bean @Bean
RouterFunction<ServerResponse> getEmployeeByIdRoute() { RouterFunction<ServerResponse> getEmployeeByIdRoute() {
return route(GET("/employees/{id}"), return route(GET("/employees/{id}"),
req -> ok().body( req -> ok().body(
employeeRepository().findEmployeeById(req.pathVariable("id")), Employee.class)); employeeRepository().findEmployeeById(req.pathVariable("id")), Employee.class));
} }
@Bean @Bean
RouterFunction<ServerResponse> updateEmployeeRoute() { RouterFunction<ServerResponse> updateEmployeeRoute() {
return route(POST("/employees/update"), return route(POST("/employees/update"),
req -> req.body(toMono(Employee.class)) req -> req.body(toMono(Employee.class))
.doOnNext(employeeRepository()::updateEmployee) .doOnNext(employeeRepository()::updateEmployee)
.then(ok().build())); .then(ok().build()));
} }
@Bean @Bean
RouterFunction<ServerResponse> composedRoutes() { RouterFunction<ServerResponse> composedRoutes() {
return return
route(GET("/employees"), route(GET("/employees"),
req -> ok().body( req -> ok().body(
employeeRepository().findAllEmployees(), Employee.class)) employeeRepository().findAllEmployees(), Employee.class))
.and(route(GET("/employees/{id}"), .and(route(GET("/employees/{id}"),
req -> ok().body( req -> ok().body(
employeeRepository().findEmployeeById(req.pathVariable("id")), Employee.class))) employeeRepository().findEmployeeById(req.pathVariable("id")), Employee.class)))
.and(route(POST("/employees/update"), .and(route(POST("/employees/update"),
req -> req.body(toMono(Employee.class)) req -> req.body(toMono(Employee.class))
.doOnNext(employeeRepository()::updateEmployee) .doOnNext(employeeRepository()::updateEmployee)
.then(ok().build()))); .then(ok().build())));
} }
@Bean @Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.csrf() http
.disable() .csrf().disable()
.authorizeExchange() .authorizeExchange()
.anyExchange() .anyExchange().permitAll();
.permitAll();
return http.build(); return http.build();
} }
} }

View File

@ -2,27 +2,27 @@ package com.baeldung.reactive.webflux.annotation;
import com.baeldung.reactive.webflux.Employee; import com.baeldung.reactive.webflux.Employee;
import com.baeldung.reactive.webflux.EmployeeRepository; import com.baeldung.reactive.webflux.EmployeeRepository;
import com.baeldung.reactive.webflux.annotation.EmployeeSpringApplication;
import org.junit.FixMethodOrder;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.util.ArrayList; import java.util.Arrays;
import java.util.List; import java.util.List;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes= EmployeeSpringApplication.class) @SpringBootTest(webEnvironment = RANDOM_PORT, classes = EmployeeSpringApplication.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class EmployeeControllerIntegrationTest { public class EmployeeControllerIntegrationTest {
@Autowired @Autowired
@ -35,40 +35,62 @@ public class EmployeeControllerIntegrationTest {
public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() { public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() {
Employee employee = new Employee("1", "Employee 1 Name"); Employee employee = new Employee("1", "Employee 1 Name");
given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee)); given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee));
testClient.get() testClient.get()
.uri("/employees/1") .uri("/employees/1")
.exchange() .exchange()
.expectStatus() .expectStatus().isOk()
.isOk() .expectBody(Employee.class).isEqualTo(employee);
.expectBody(Employee.class)
.isEqualTo(employee);
} }
@Test @Test
public void whenGetAllEmployees_thenCorrectEmployees() { public void whenGetAllEmployees_thenCorrectEmployees() {
List<Employee> employeeList = Arrays.asList(
List<Employee> employeeList = new ArrayList<>(); new Employee("1", "Employee 1 Name"),
new Employee("2", "Employee 2 Name"),
Employee employee1 = new Employee("1", "Employee 1 Name"); new Employee("3", "Employee 3 Name")
Employee employee2 = new Employee("2", "Employee 2 Name"); );
Employee employee3 = new Employee("3", "Employee 3 Name");
employeeList.add(employee1);
employeeList.add(employee2);
employeeList.add(employee3);
Flux<Employee> employeeFlux = Flux.fromIterable(employeeList); Flux<Employee> employeeFlux = Flux.fromIterable(employeeList);
given(employeeRepository.findAllEmployees()).willReturn(employeeFlux); given(employeeRepository.findAllEmployees()).willReturn(employeeFlux);
testClient.get() testClient.get()
.uri("/employees") .uri("/employees")
.exchange() .exchange()
.expectStatus() .expectStatus().isOk()
.isOk() .expectBodyList(Employee.class).isEqualTo(employeeList);
.expectBodyList(Employee.class) }
.hasSize(3)
.isEqualTo(employeeList); @Test
@WithMockUser(username = "admin", roles = { "ADMIN" })
public void givenValidUser_whenUpdateEmployee_thenEmployeeUpdated() {
Employee employee = new Employee("10", "Employee 10 Updated");
given(employeeRepository.updateEmployee(employee)).willReturn(Mono.just(employee));
testClient.post()
.uri("/employees/update")
.body(Mono.just(employee), Employee.class)
.exchange()
.expectStatus().isOk()
.expectBody(Employee.class).isEqualTo(employee);
verify(employeeRepository).updateEmployee(employee);
}
@Test
@WithMockUser
public void givenInvalidUser_whenUpdateEmployee_thenForbidden() {
Employee employee = new Employee("10", "Employee 10 Updated");
testClient.post()
.uri("/employees/update")
.body(Mono.just(employee), Employee.class)
.exchange()
.expectStatus().isForbidden();
verifyNoInteractions(employeeRepository);
} }
} }

View File

@ -2,10 +2,8 @@ package com.baeldung.reactive.webflux.functional;
import com.baeldung.reactive.webflux.Employee; import com.baeldung.reactive.webflux.Employee;
import com.baeldung.reactive.webflux.EmployeeRepository; import com.baeldung.reactive.webflux.EmployeeRepository;
import org.junit.FixMethodOrder;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
@ -19,10 +17,10 @@ import java.util.List;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = EmployeeSpringFunctionalApplication.class) @SpringBootTest(webEnvironment = RANDOM_PORT, classes = EmployeeSpringFunctionalApplication.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class EmployeeSpringFunctionalIntegrationTest { public class EmployeeSpringFunctionalIntegrationTest {
@Autowired @Autowired
@ -34,58 +32,54 @@ public class EmployeeSpringFunctionalIntegrationTest {
@Test @Test
public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() { public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() {
WebTestClient client = WebTestClient WebTestClient client = WebTestClient
.bindToRouterFunction(config.getEmployeeByIdRoute()) .bindToRouterFunction(config.getEmployeeByIdRoute())
.build(); .build();
Employee employee = new Employee("1", "Employee 1"); Employee employee = new Employee("1", "Employee 1");
given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee)); given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee));
client.get() client.get()
.uri("/employees/1") .uri("/employees/1")
.exchange() .exchange()
.expectStatus() .expectStatus().isOk()
.isOk() .expectBody(Employee.class).isEqualTo(employee);
.expectBody(Employee.class)
.isEqualTo(employee);
} }
@Test @Test
public void whenGetAllEmployees_thenCorrectEmployees() { public void whenGetAllEmployees_thenCorrectEmployees() {
WebTestClient client = WebTestClient WebTestClient client = WebTestClient
.bindToRouterFunction(config.getAllEmployeesRoute()) .bindToRouterFunction(config.getAllEmployeesRoute())
.build(); .build();
List<Employee> employees = Arrays.asList( List<Employee> employees = Arrays.asList(
new Employee("1", "Employee 1"), new Employee("1", "Employee 1"),
new Employee("2", "Employee 2")); new Employee("2", "Employee 2")
);
Flux<Employee> employeeFlux = Flux.fromIterable(employees); Flux<Employee> employeeFlux = Flux.fromIterable(employees);
given(employeeRepository.findAllEmployees()).willReturn(employeeFlux); given(employeeRepository.findAllEmployees()).willReturn(employeeFlux);
client.get() client.get()
.uri("/employees") .uri("/employees")
.exchange() .exchange()
.expectStatus() .expectStatus().isOk()
.isOk() .expectBodyList(Employee.class).isEqualTo(employees);
.expectBodyList(Employee.class)
.isEqualTo(employees);
} }
@Test @Test
public void whenUpdateEmployee_thenEmployeeUpdated() { public void whenUpdateEmployee_thenEmployeeUpdated() {
WebTestClient client = WebTestClient WebTestClient client = WebTestClient
.bindToRouterFunction(config.updateEmployeeRoute()) .bindToRouterFunction(config.updateEmployeeRoute())
.build(); .build();
Employee employee = new Employee("1", "Employee 1 Updated"); Employee employee = new Employee("1", "Employee 1 Updated");
client.post() client.post()
.uri("/employees/update") .uri("/employees/update")
.body(Mono.just(employee), Employee.class) .body(Mono.just(employee), Employee.class)
.exchange() .exchange()
.expectStatus() .expectStatus().isOk();
.isOk();
verify(employeeRepository).updateEmployee(employee); verify(employeeRepository).updateEmployee(employee);
} }