Merge branch 'master' of https://github.com/eugenp/tutorials into BAEL-4461-3
This commit is contained in:
commit
7c3bccab44
|
@ -147,7 +147,15 @@
|
||||||
<version>${project-reactor-test}</version>
|
<version>${project-reactor-test}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
}
|
|
@ -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<String, WebSocketHandler> 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.baeldung.reactive.webflux;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/employees")
|
||||||
|
public class EmployeeController {
|
||||||
|
|
||||||
|
private EmployeeRepository employeeRepository;
|
||||||
|
|
||||||
|
public EmployeeController(EmployeeRepository employeeRepository) {
|
||||||
|
this.employeeRepository = employeeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
private Mono<Employee> getEmployeeById(@PathVariable String id) {
|
||||||
|
return employeeRepository.findEmployeeById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
private Flux<Employee> getAllEmployees() {
|
||||||
|
return employeeRepository.findAllEmployees();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/update")
|
||||||
|
private Mono<Employee> updateEmployee(@RequestBody Employee employee) {
|
||||||
|
return employeeRepository.updateEmployee(employee);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
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<String,Employee> employeeData;
|
||||||
|
|
||||||
|
static Map<String,String> 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<Employee> findEmployeeById(String id)
|
||||||
|
{
|
||||||
|
return Mono.just(employeeData.get(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Flux<Employee> findAllEmployees()
|
||||||
|
{
|
||||||
|
return Flux.fromIterable(employeeData.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<Employee> updateEmployee(Employee employee)
|
||||||
|
{
|
||||||
|
Employee existingEmployee=employeeData.get(employee.getId());
|
||||||
|
if(existingEmployee!=null)
|
||||||
|
{
|
||||||
|
existingEmployee.setName(employee.getName());
|
||||||
|
}
|
||||||
|
return Mono.just(existingEmployee);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<Employee> employeeMono = client.get()
|
||||||
|
.uri("/employees/{id}", "1")
|
||||||
|
.retrieve()
|
||||||
|
.bodyToMono(Employee.class);
|
||||||
|
|
||||||
|
employeeMono.subscribe(System.out::println);
|
||||||
|
|
||||||
|
Flux<Employee> employeeFlux = client.get()
|
||||||
|
.uri("/employees")
|
||||||
|
.retrieve()
|
||||||
|
.bodyToFlux(Employee.class);
|
||||||
|
|
||||||
|
employeeFlux.subscribe(System.out::println);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.baeldung.reactive.webflux;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
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;
|
||||||
|
import org.springframework.security.core.userdetails.User;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||||
|
|
||||||
|
@EnableWebFluxSecurity
|
||||||
|
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.csrf()
|
||||||
|
.disable()
|
||||||
|
.authorizeExchange()
|
||||||
|
.pathMatchers(HttpMethod.POST, "/employees/update")
|
||||||
|
.hasRole("ADMIN")
|
||||||
|
.pathMatchers("/**")
|
||||||
|
.permitAll()
|
||||||
|
.and()
|
||||||
|
.httpBasic();
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(); // to subscribe and return the value
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +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<Void> handle(WebSocketSession webSocketSession) {
|
||||||
|
|
||||||
|
Flux<String> 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)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package com.baeldung.reactive.webflux;
|
||||||
|
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
|
||||||
|
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.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;
|
||||||
|
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
public class EmployeeControllerUnitTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private WebTestClient testClient;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private EmployeeRepository employeeRepository;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
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
|
||||||
|
public void whenGetAllEmployees_thenCorrectEmployees() {
|
||||||
|
|
||||||
|
List<Employee> 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<Employee> employeeFlux = Flux.fromIterable(employeeList);
|
||||||
|
|
||||||
|
given(employeeRepository.findAllEmployees()).willReturn(employeeFlux);
|
||||||
|
testClient.get()
|
||||||
|
.uri("/employees")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus()
|
||||||
|
.isOk()
|
||||||
|
.expectBodyList(Employee.class)
|
||||||
|
.hasSize(3)
|
||||||
|
.isEqualTo(employeeList);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,15 +18,12 @@ public class UserController {
|
||||||
|
|
||||||
@RequestMapping("/login")
|
@RequestMapping("/login")
|
||||||
public boolean login(@RequestBody User user) {
|
public boolean login(@RequestBody User user) {
|
||||||
if(user.getUserName().equals("user") && user.getPassword().equals("password")) {
|
return user.getUserName().equals("user") && user.getPassword().equals("password");
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping("/user")
|
@RequestMapping("/user")
|
||||||
public Principal user(HttpServletRequest request) {
|
public Principal user(HttpServletRequest request) {
|
||||||
String authToken = request.getHeader("Authorization").substring("Basic".length()).trim();
|
String authToken = request.getHeader("Authorization").substring("Basic".length()).trim();
|
||||||
return () -> new String(Base64.getDecoder().decode(authToken)).split(":")[0];
|
return () -> new String(Base64.getDecoder().decode(authToken)).split(":")[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue