From f1a423571913e5c7d1df36df03e130337d27751c Mon Sep 17 00:00:00 2001 From: Dhawal Kapil Date: Mon, 11 Jun 2018 01:45:01 +0530 Subject: [PATCH 1/7] BAEL-1814 Guide to Spring Webflux -Added files for Employee reactive application -Updated pom.xml for Spring Security --- spring-5-reactive/pom.xml | 10 +++- .../baeldung/reactive/webflux/Employee.java | 17 ++++++ .../reactive/webflux/EmployeeConfig.java | 33 +++++++++++ .../reactive/webflux/EmployeeController.java | 37 ++++++++++++ .../reactive/webflux/EmployeeRepository.java | 59 +++++++++++++++++++ .../webflux/EmployeeSpringApplication.java | 17 ++++++ .../reactive/webflux/EmployeeWebClient.java | 28 +++++++++ .../webflux/EmployeeWebSecurityConfig.java | 36 +++++++++++ .../webflux/EmployeeWebSocketClient.java | 21 +++++++ .../webflux/EmployeeWebSocketHandler.java | 19 ++++++ 10 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/Employee.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeConfig.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeSpringApplication.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebClient.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketClient.java create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java diff --git a/spring-5-reactive/pom.xml b/spring-5-reactive/pom.xml index 8b40ccee007e..850c0d574319 100644 --- a/spring-5-reactive/pom.xml +++ b/spring-5-reactive/pom.xml @@ -147,7 +147,15 @@ ${project-reactor-test} test - + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.security + spring-security-test + test + diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/Employee.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/Employee.java new file mode 100644 index 000000000000..6a0355565493 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/Employee.java @@ -0,0 +1,17 @@ +package com.baeldung.reactive.webflux; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Employee { + + private String id; + private String name; + + // standard getters and setters + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeConfig.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeConfig.java new file mode 100644 index 000000000000..082be6869851 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeConfig.java @@ -0,0 +1,33 @@ +package com.baeldung.reactive.webflux; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.HandlerMapping; +import org.springframework.web.reactive.config.EnableWebFlux; +import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; +import org.springframework.web.reactive.socket.WebSocketHandler; +import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter; + +@Configuration +@EnableWebFlux +public class EmployeeConfig { + + @Bean + public HandlerMapping handlerMapping() { + Map map = new HashMap<>(); + map.put("/employee-feed", new EmployeeWebSocketHandler()); + + SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); + mapping.setUrlMap(map); + mapping.setOrder(10); + return mapping; + } + + @Bean + public WebSocketHandlerAdapter handlerAdapter() { + return new WebSocketHandlerAdapter(); + } +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java new file mode 100644 index 000000000000..9aedd1249c46 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java @@ -0,0 +1,37 @@ +package com.baeldung.reactive.webflux; + +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.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@RestController +@RequestMapping("/employees") +public class EmployeeController { + + @Autowired + private EmployeeRepository employeeRepository; + + @GetMapping("/{id}") + private Mono getEmployeeById(@PathVariable String id) + { + return employeeRepository.findEmployeeById(id); + } + + @GetMapping + private Flux getAllEmployees() + { + return employeeRepository.findAllEmployees(); + } + + @GetMapping("/access-key/{id}") + private Mono getAdminAllEmployees(@PathVariable String id) + { + return employeeRepository.findEmployeeAccessKey(id); + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java new file mode 100644 index 000000000000..140ec3dd0865 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java @@ -0,0 +1,59 @@ +package com.baeldung.reactive.webflux; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.stereotype.Repository; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Repository +public class EmployeeRepository { + + static Map employeeData; + + static Map employeeAccessData; + + static + { + employeeData = new HashMap<>(); + employeeData.put("1",new Employee("1","Employee 1")); + employeeData.put("2",new Employee("2","Employee 2")); + employeeData.put("3",new Employee("3","Employee 3")); + employeeData.put("4",new Employee("4","Employee 4")); + employeeData.put("5",new Employee("5","Employee 5")); + employeeData.put("6",new Employee("6","Employee 6")); + employeeData.put("7",new Employee("7","Employee 7")); + employeeData.put("8",new Employee("8","Employee 8")); + employeeData.put("9",new Employee("9","Employee 9")); + employeeData.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 findEmployeeById(String id) + { + return Mono.just(employeeData.get(id)); + } + + public Flux findAllEmployees() + { + return Flux.fromIterable(employeeData.values()); + } + + public Mono findEmployeeAccessKey(String id) + { + return Mono.just(employeeAccessData.get(id)); + } +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeSpringApplication.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeSpringApplication.java new file mode 100644 index 000000000000..54b23a18de5a --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeSpringApplication.java @@ -0,0 +1,17 @@ +package com.baeldung.reactive.webflux; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class EmployeeSpringApplication { + + public static void main(String[] args) { + + SpringApplication.run(EmployeeSpringApplication.class, args); + + EmployeeWebClient employeeWebClient = new EmployeeWebClient(); + employeeWebClient.consume(); + } + +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebClient.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebClient.java new file mode 100644 index 000000000000..45d42ecda99c --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebClient.java @@ -0,0 +1,28 @@ +package com.baeldung.reactive.webflux; + +import org.springframework.web.reactive.function.client.WebClient; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class EmployeeWebClient { + + WebClient client = WebClient.create("http://localhost:8080"); + + public void consume() { + + Mono employeeMono = client.get() + .uri("/employees/{id}", "1") + .retrieve() + .bodyToMono(Employee.class); + + employeeMono.subscribe(System.out::println); + + Flux employeeFlux = client.get() + .uri("/employees") + .retrieve() + .bodyToFlux(Employee.class); + + employeeFlux.subscribe(System.out::println); + } +} \ No newline at end of file diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java new file mode 100644 index 000000000000..fdfcb48b181c --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java @@ -0,0 +1,36 @@ +package com.baeldung.reactive.webflux; + +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.server.SecurityWebFilterChain; + +@EnableWebFluxSecurity +@EnableReactiveMethodSecurity +public class EmployeeWebSecurityConfig { + + @Bean + public MapReactiveUserDetailsService userDetailsService() { + UserDetails user = User.withDefaultPasswordEncoder() + .username("admin") + .password("password") + .roles("ADMIN") + .build(); + return new MapReactiveUserDetailsService(user); + } + + @Bean + public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { + http.authorizeExchange() + .pathMatchers("/employees-secured/access-key/**") + .hasRole("ADMIN") + .anyExchange() + .authenticated() + .and().formLogin(); + return http.build(); + } +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketClient.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketClient.java new file mode 100644 index 000000000000..11e53cb16b7a --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketClient.java @@ -0,0 +1,21 @@ +package com.baeldung.reactive.webflux; + +import java.net.URI; + +import org.springframework.web.reactive.socket.WebSocketMessage; +import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient; +import org.springframework.web.reactive.socket.client.WebSocketClient; + +public class EmployeeWebSocketClient { + + public static void main(String[] args) { + + WebSocketClient client = new ReactorNettyWebSocketClient(); + + client.execute(URI.create("ws://localhost:8080/employee-feed"), session -> session.receive() + .map(WebSocketMessage::getPayloadAsText) + .doOnNext(System.out::println) + .then()) + .block(); + } +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java new file mode 100644 index 000000000000..4b3bb511d7a5 --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java @@ -0,0 +1,19 @@ +package com.baeldung.reactive.webflux; + +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.socket.WebSocketHandler; +import org.springframework.web.reactive.socket.WebSocketSession; + +import reactor.core.publisher.Mono; + +@Component +public class EmployeeWebSocketHandler implements WebSocketHandler { + + @Override + public Mono handle(WebSocketSession webSocketSession) { + return webSocketSession.send(new EmployeeRepository().findAllEmployees() + .map(Employee::getName) + .map(webSocketSession::textMessage)); + } + +} From 6b31d8a0c192df2736001ba526dcbb1e85112d8b Mon Sep 17 00:00:00 2001 From: Dhawal Kapil Date: Mon, 11 Jun 2018 13:15:16 +0530 Subject: [PATCH 2/7] BAEL-1814 Guide to Spring Webflux -Added EmployeeControllerTest -Updated method name in EmployeeController and corrected secured url in EmployeeWebSecurityConfig --- .../reactive/webflux/EmployeeController.java | 2 +- .../webflux/EmployeeWebSecurityConfig.java | 2 +- .../webflux/EmployeeControllerTest.java | 85 +++++++++++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java index 9aedd1249c46..4d529558e549 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java @@ -29,7 +29,7 @@ private Flux getAllEmployees() } @GetMapping("/access-key/{id}") - private Mono getAdminAllEmployees(@PathVariable String id) + private Mono getEmployeeAccessKey(@PathVariable String id) { return employeeRepository.findEmployeeAccessKey(id); } diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java index fdfcb48b181c..d7758e7db5c3 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java @@ -26,7 +26,7 @@ public MapReactiveUserDetailsService userDetailsService() { @Bean public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http.authorizeExchange() - .pathMatchers("/employees-secured/access-key/**") + .pathMatchers("/employees/access-key/**") .hasRole("ADMIN") .anyExchange() .authenticated() diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java new file mode 100644 index 000000000000..d40329ab2c2f --- /dev/null +++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java @@ -0,0 +1,85 @@ +package com.baeldung.reactive.webflux; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.BDDMockito.given; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +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.web.reactive.server.WebTestClient; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@RunWith(SpringRunner.class) +@WebFluxTest(EmployeeController.class) +public class EmployeeControllerTest { + + @Autowired + private WebTestClient testClient; + + @MockBean + private EmployeeRepository employeeRepository; + + @Test + @WithMockUser + public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() { + + Employee employee = new Employee("1", "Employee 1 Name"); + given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee)); + testClient.get() + .uri("/employees/1") + .exchange() + .expectStatus() + .isOk() + .expectBody(Employee.class) + .isEqualTo(employee); + } + + @Test + @WithMockUser + public void whenGetAllEmployees_thenCorrectEmployees() { + + List employeeList = new ArrayList<>(); + + Employee employee1 = new Employee("1", "Employee 1 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 employeeFlux = Flux.fromIterable(employeeList); + + given(employeeRepository.findAllEmployees()).willReturn(employeeFlux); + testClient.get() + .uri("/employees") + .exchange() + .expectStatus() + .isOk() + .expectBodyList(Employee.class) + .hasSize(3) + .isEqualTo(employeeList); + } + + @Test + @WithMockUser + public void givenEmployeeId_whenGetEmployeeAccessKey_thenCorrectAccessKey() { + + String employeeAccessKey = "Employee Access Key"; + given(employeeRepository.findEmployeeAccessKey("1")).willReturn(Mono.just(employeeAccessKey)); + testClient.get() + .uri("/employees/access-key/1") + .exchange() + .expectStatus() + .isOk() + .expectBody(String.class) + .isEqualTo(employeeAccessKey); + } +} From 3c13556a1d2f53e1130f8484f4dcf0e81f6e5820 Mon Sep 17 00:00:00 2001 From: Dhawal Kapil Date: Mon, 11 Jun 2018 16:45:43 +0530 Subject: [PATCH 3/7] BAEL-1814 Guide to spring webflux -Fixed security config, now only specific url prompts for authentication and not all endpoints -Removed @WithMockUser as it is not needed now --- .../reactive/webflux/EmployeeWebSecurityConfig.java | 5 ++--- .../baeldung/reactive/webflux/EmployeeControllerTest.java | 7 ++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java index d7758e7db5c3..2a1620b8649e 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java @@ -28,9 +28,8 @@ public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) http.authorizeExchange() .pathMatchers("/employees/access-key/**") .hasRole("ADMIN") - .anyExchange() - .authenticated() - .and().formLogin(); + .pathMatchers("/**").permitAll() + .and().httpBasic(); return http.build(); } } diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java index d40329ab2c2f..5f28b1ff8ffe 100644 --- a/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java +++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java @@ -1,15 +1,15 @@ package com.baeldung.reactive.webflux; +import static org.mockito.BDDMockito.given; + import java.util.ArrayList; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; -import static org.mockito.BDDMockito.given; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; 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.web.reactive.server.WebTestClient; @@ -27,7 +27,6 @@ public class EmployeeControllerTest { private EmployeeRepository employeeRepository; @Test - @WithMockUser public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() { Employee employee = new Employee("1", "Employee 1 Name"); @@ -42,7 +41,6 @@ public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() { } @Test - @WithMockUser public void whenGetAllEmployees_thenCorrectEmployees() { List employeeList = new ArrayList<>(); @@ -69,7 +67,6 @@ public void whenGetAllEmployees_thenCorrectEmployees() { } @Test - @WithMockUser public void givenEmployeeId_whenGetEmployeeAccessKey_thenCorrectAccessKey() { String employeeAccessKey = "Employee Access Key"; From 425d2cbcf1942e49a6d76681421f8f3a40e29ebd Mon Sep 17 00:00:00 2001 From: Dhawal Kapil Date: Fri, 15 Jun 2018 00:45:20 +0530 Subject: [PATCH 4/7] BAEL-1814 Guide To Webflux -Feedback incorporation --- .../reactive/webflux/EmployeeController.java | 25 +++++++++--------- .../webflux/EmployeeCreationEvent.java | 11 ++++++++ .../reactive/webflux/EmployeeRepository.java | 9 +++++-- .../webflux/EmployeeWebSecurityConfig.java | 15 ++++++----- .../webflux/EmployeeWebSocketClient.java | 2 +- .../webflux/EmployeeWebSocketHandler.java | 26 ++++++++++++++++--- .../webflux/EmployeeControllerTest.java | 14 ---------- 7 files changed, 64 insertions(+), 38 deletions(-) create mode 100644 spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeCreationEvent.java diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java index 4d529558e549..98b16dafab43 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeController.java @@ -1,8 +1,9 @@ package com.baeldung.reactive.webflux; -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.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -13,25 +14,25 @@ @RequestMapping("/employees") public class EmployeeController { - @Autowired private EmployeeRepository employeeRepository; - + + public EmployeeController(EmployeeRepository employeeRepository) { + this.employeeRepository = employeeRepository; + } + @GetMapping("/{id}") - private Mono getEmployeeById(@PathVariable String id) - { + private Mono getEmployeeById(@PathVariable String id) { return employeeRepository.findEmployeeById(id); } - + @GetMapping - private Flux getAllEmployees() - { + private Flux getAllEmployees() { return employeeRepository.findAllEmployees(); } - @GetMapping("/access-key/{id}") - private Mono getEmployeeAccessKey(@PathVariable String id) - { - return employeeRepository.findEmployeeAccessKey(id); + @PostMapping("/update") + private Mono updateEmployee(@RequestBody Employee employee) { + return employeeRepository.updateEmployee(employee); } } diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeCreationEvent.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeCreationEvent.java new file mode 100644 index 000000000000..7be088f073ea --- /dev/null +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeCreationEvent.java @@ -0,0 +1,11 @@ +package com.baeldung.reactive.webflux; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class EmployeeCreationEvent { + private String employeeId; + private String creationTime; +} diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java index 140ec3dd0865..a407c76fa8d3 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeRepository.java @@ -52,8 +52,13 @@ public Flux findAllEmployees() return Flux.fromIterable(employeeData.values()); } - public Mono findEmployeeAccessKey(String id) + public Mono updateEmployee(Employee employee) { - return Mono.just(employeeAccessData.get(id)); + Employee existingEmployee=employeeData.get(employee.getId()); + if(existingEmployee!=null) + { + existingEmployee.setName(employee.getName()); + } + return Mono.just(existingEmployee); } } diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java index 2a1620b8649e..7922e6ba44f2 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSecurityConfig.java @@ -1,7 +1,7 @@ package com.baeldung.reactive.webflux; import org.springframework.context.annotation.Bean; -import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; +import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; @@ -10,7 +10,6 @@ import org.springframework.security.web.server.SecurityWebFilterChain; @EnableWebFluxSecurity -@EnableReactiveMethodSecurity public class EmployeeWebSecurityConfig { @Bean @@ -25,11 +24,15 @@ public MapReactiveUserDetailsService userDetailsService() { @Bean public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { - http.authorizeExchange() - .pathMatchers("/employees/access-key/**") + http.csrf() + .disable() + .authorizeExchange() + .pathMatchers(HttpMethod.POST, "/employees/update") .hasRole("ADMIN") - .pathMatchers("/**").permitAll() - .and().httpBasic(); + .pathMatchers("/**") + .permitAll() + .and() + .httpBasic(); return http.build(); } } diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketClient.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketClient.java index 11e53cb16b7a..4571cadc47a0 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketClient.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketClient.java @@ -16,6 +16,6 @@ public static void main(String[] args) { .map(WebSocketMessage::getPayloadAsText) .doOnNext(System.out::println) .then()) - .block(); + .block(); // to subscribe and return the value } } diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java index 4b3bb511d7a5..2bd3fc00d5f9 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java @@ -1,19 +1,39 @@ package com.baeldung.reactive.webflux; +import static java.time.LocalDateTime.now; +import static java.util.UUID.randomUUID; + +import java.time.Duration; + import org.springframework.stereotype.Component; import org.springframework.web.reactive.socket.WebSocketHandler; import org.springframework.web.reactive.socket.WebSocketSession; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @Component public class EmployeeWebSocketHandler implements WebSocketHandler { + ObjectMapper om = new ObjectMapper(); + @Override public Mono handle(WebSocketSession webSocketSession) { - return webSocketSession.send(new EmployeeRepository().findAllEmployees() - .map(Employee::getName) - .map(webSocketSession::textMessage)); + + Flux employeeCreationEvent = Flux.generate(sink -> { + EmployeeCreationEvent event = new EmployeeCreationEvent(randomUUID().toString(), now().toString()); + try { + sink.next(om.writeValueAsString(event)); + } catch (JsonProcessingException e) { + sink.error(e); + } + }); + + return webSocketSession.send(employeeCreationEvent.map(webSocketSession::textMessage) + .delayElements(Duration.ofSeconds(1))); } } diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java index 5f28b1ff8ffe..ad4ca3ff63db 100644 --- a/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java +++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java @@ -65,18 +65,4 @@ public void whenGetAllEmployees_thenCorrectEmployees() { .hasSize(3) .isEqualTo(employeeList); } - - @Test - public void givenEmployeeId_whenGetEmployeeAccessKey_thenCorrectAccessKey() { - - String employeeAccessKey = "Employee Access Key"; - given(employeeRepository.findEmployeeAccessKey("1")).willReturn(Mono.just(employeeAccessKey)); - testClient.get() - .uri("/employees/access-key/1") - .exchange() - .expectStatus() - .isOk() - .expectBody(String.class) - .isEqualTo(employeeAccessKey); - } } From 5b69bce7d320050e123201e4e417cfb353d21c41 Mon Sep 17 00:00:00 2001 From: Dhawal Kapil Date: Mon, 18 Jun 2018 13:48:30 +0530 Subject: [PATCH 5/7] BAEL-1814 Spring Webflux Guide -Formatted coded for EmployeeWebSocketHandler. --- .../baeldung/reactive/webflux/EmployeeWebSocketHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java index 2bd3fc00d5f9..caa2a38b65cb 100644 --- a/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/webflux/EmployeeWebSocketHandler.java @@ -32,8 +32,8 @@ public Mono handle(WebSocketSession webSocketSession) { } }); - return webSocketSession.send(employeeCreationEvent.map(webSocketSession::textMessage) + return webSocketSession.send(employeeCreationEvent + .map(webSocketSession::textMessage) .delayElements(Duration.ofSeconds(1))); } - } From 5a350bec55163610f831600addc76fd852889deb Mon Sep 17 00:00:00 2001 From: Dhawal Kapil Date: Fri, 13 Jul 2018 17:01:25 +0530 Subject: [PATCH 6/7] Update and rename EmployeeControllerTest.java to EmployeeControllerUnitTest.java --- ...loyeeControllerTest.java => EmployeeControllerUnitTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/{EmployeeControllerTest.java => EmployeeControllerUnitTest.java} (94%) diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java similarity index 94% rename from spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java rename to spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java index ad4ca3ff63db..4934488b4fef 100644 --- a/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerTest.java +++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java @@ -18,7 +18,7 @@ @RunWith(SpringRunner.class) @WebFluxTest(EmployeeController.class) -public class EmployeeControllerTest { +public class EmployeeControllerUnitTest { @Autowired private WebTestClient testClient; From cd99fc9d3ed7f16eb28d7294ee7ca8c824bbaab2 Mon Sep 17 00:00:00 2001 From: Dhawal Kapil Date: Sat, 14 Jul 2018 13:20:28 +0530 Subject: [PATCH 7/7] BAEL-1814 Guide to spring webflux -Fixed EmployeeControllerUnitTest.java --- .../reactive/webflux/EmployeeControllerUnitTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java index 4934488b4fef..640f28c3319a 100644 --- a/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java +++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/webflux/EmployeeControllerUnitTest.java @@ -5,10 +5,12 @@ import java.util.ArrayList; import java.util.List; +import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; 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; @@ -17,7 +19,8 @@ import reactor.core.publisher.Mono; @RunWith(SpringRunner.class) -@WebFluxTest(EmployeeController.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) public class EmployeeControllerUnitTest { @Autowired @@ -30,6 +33,7 @@ public class EmployeeControllerUnitTest { public void givenEmployeeId_whenGetEmployeeById_thenCorrectEmployee() { Employee employee = new Employee("1", "Employee 1 Name"); + given(employeeRepository.findEmployeeById("1")).willReturn(Mono.just(employee)); testClient.get() .uri("/employees/1")