Skip to content
Snippets Groups Projects
Verified Commit de2c454b authored by Sebastian Bergandy's avatar Sebastian Bergandy :keyboard:
Browse files

OZG-7232 switch from WebClient to RestClient

Subtask: OZG-8002
parent 6fa17973
No related branches found
No related tags found
1 merge request!4OZG-7232 SmartDocuments zertifikatbasierte Authentifizierung
......@@ -25,6 +25,7 @@ package de.ozgcloud.document.bescheid.smartdocuments;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Optional;
......@@ -34,14 +35,15 @@ import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.client.RestClient;
import org.w3c.dom.Document;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
......@@ -69,7 +71,6 @@ import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import reactor.core.publisher.Mono;
@Log4j2
@Service
......@@ -87,7 +88,7 @@ class SmartDocumentsBescheidRemoteService implements BescheidRemoteService {
private static final MediaType JSON_MEDIA_TYPE_FOR_SD = MediaType.APPLICATION_JSON_UTF8;
@Qualifier("smartDocuments")
private final WebClient smartDocumentsWebClient;
private final RestClient smartDocumentsRestClient;
private final SmartDocumentsProperties properties;
......@@ -96,19 +97,24 @@ class SmartDocumentsBescheidRemoteService implements BescheidRemoteService {
var sdRequest = createRequest(request, vorgang);
LOG.debug(() -> buildLogRequest(sdRequest));
return smartDocumentsWebClient.post().accept(MediaType.APPLICATION_JSON)
var response = smartDocumentsRestClient.post().accept(MediaType.APPLICATION_JSON)
.contentType(JSON_MEDIA_TYPE_FOR_SD)
.bodyValue(sdRequest)
.body(sdRequest)
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, this::handleClientError)
.bodyToMono(SmartDocumentsResponse.class)
.map(response -> buildBescheid(request, response))
.block();
.toEntity(SmartDocumentsResponse.class)
.getBody();
return buildBescheid(request, response);
}
Mono<Throwable> handleClientError(ClientResponse response) {
return response.body(BodyExtractors.toMono(String.class))
.map(content -> new TechnicalException("Client-Error: " + content));
void handleClientError(HttpRequest request, ClientHttpResponse response) {
try {
var responseBody = IOUtils.toString(response.getBody(), StandardCharsets.UTF_8);
throw new TechnicalException("Client-Error: " + response.getStatusCode() + " " + responseBody);
} catch (IOException e) {
throw new TechnicalException("Couldn't read response body", e);
}
}
private String buildLogRequest(SmartDocumentsRequest request) {
......
......@@ -23,16 +23,20 @@
*/
package de.ozgcloud.document.bescheid.smartdocuments;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.CredentialsProvider;
import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
import org.apache.hc.core5.http.HttpHost;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.ExchangeFilterFunctions;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
import reactor.netty.transport.ProxyProvider;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestClient;
@Configuration
@ConditionalOnProperty("ozgcloud.bescheid.smart-documents.url")
......@@ -42,35 +46,43 @@ class SmartDocumentsConfiguration {
private SmartDocumentsProperties properties;
@Bean("smartDocuments")
WebClient smartDocumentsWebClient() {
ReactorClientHttpConnector connector = new ReactorClientHttpConnector(buildHttpClient());
return WebClient.builder()
RestClient smartDocumentsRestClient() {
return RestClient.builder()
.requestFactory(new HttpComponentsClientHttpRequestFactory(buildHttpClient()))
.baseUrl(properties.getUrl())
.filter(ExchangeFilterFunctions.basicAuthentication(properties.getBasicAuth().getUsername(), properties.getBasicAuth().getPassword()))
.clientConnector(connector)
.defaultHeaders(headers -> headers.setBasicAuth(properties.getBasicAuth().getUsername(), properties.getBasicAuth().getPassword()))
.build();
}
private HttpClient buildHttpClient() {
CloseableHttpClient buildHttpClient() {
if (properties.getProxy() != null) {
return createProxyHttpClient();
} else {
return createNoProxyHttpClient();
}
return createNoProxyHttpClient();
}
private HttpClient createNoProxyHttpClient() {
return HttpClient.create();
CloseableHttpClient createNoProxyHttpClient() {
return HttpClients.createDefault();
}
private HttpClient createProxyHttpClient() {
return HttpClient.create()
.proxy(proxy -> proxy.type(ProxyProvider.Proxy.HTTP)
.host(properties.getProxy().getHost())
.port(properties.getProxy().getPort())
.username(properties.getProxy().getUsername())
.password(username -> properties.getProxy().getPassword()));
CloseableHttpClient createProxyHttpClient() {
return HttpClients.custom()
.setRoutePlanner(createProxyRoutePlanner())
.setDefaultCredentialsProvider(createProxyCredentialsProvider())
.build();
}
DefaultProxyRoutePlanner createProxyRoutePlanner() {
return new DefaultProxyRoutePlanner(new HttpHost(properties.getProxy().getHost(), properties.getProxy().getPort()));
}
CredentialsProvider createProxyCredentialsProvider() {
var host = properties.getProxy().getHost();
var port = properties.getProxy().getPort();
var username = properties.getProxy().getUsername();
var password = properties.getProxy().getPassword().toCharArray();
return CredentialsProviderBuilder.create().add(new AuthScope(host, port), username, password).build();
}
}
package de.ozgcloud.document.bescheid.smartdocuments;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import de.ozgcloud.document.bescheid.smartdocuments.SmartDocumentsProperties.ProxyConfiguration;
class SmartDocumentsConfigurationTest {
@Spy
@InjectMocks
private SmartDocumentsConfiguration smartDocumentsConfiguration;
@Mock
private SmartDocumentsProperties properties;
@Nested
class CreateProxyCredentialsProviderTest {
private final String user = "max";
private final String password = "max2";
private final String host = "test-proxy.local";
private final int port = 8080;
private final ProxyConfiguration proxyConfiguration = new ProxyConfiguration();
@BeforeEach
void setUp() {
proxyConfiguration.setHost(host);
proxyConfiguration.setPort(port);
proxyConfiguration.setUsername(user);
proxyConfiguration.setPassword(password);
when(properties.getProxy()).thenReturn(proxyConfiguration);
}
@Test
void shouldCreateWithUserAndPassword() {
var credentialsProvider = smartDocumentsConfiguration.createProxyCredentialsProvider();
var credentialsForProxy = (UsernamePasswordCredentials) credentialsProvider.getCredentials(new AuthScope(host, port), null);
assertThat(credentialsForProxy).extracting(
UsernamePasswordCredentials::getUserName,
UsernamePasswordCredentials::getUserPassword)
.containsExactly(user, password.toCharArray());
}
}
@Nested
class CreateProxyRoutePlannerTest {
private final String host = "test-proxy.local";
private final int port = 8080;
private final ProxyConfiguration proxyConfiguration = new ProxyConfiguration();
@BeforeEach
void setUp() {
proxyConfiguration.setHost(host);
proxyConfiguration.setPort(port);
when(properties.getProxy()).thenReturn(proxyConfiguration);
}
@Test
void shouldCreateWithProxySettings() throws HttpException {
var routePlanner = smartDocumentsConfiguration.createProxyRoutePlanner();
var proxyHost = routePlanner.determineRoute(new HttpHost("http://any"), new HttpClientContext()).getProxyHost();
assertThat(proxyHost).extracting(
HttpHost::getHostName,
HttpHost::getPort
).containsExactly(host, port);
}
}
@Nested
class BuildHttpClientTest {
private final CloseableHttpClient proxyHttpClient = HttpClients.createDefault();
private final CloseableHttpClient nonProxyHttpClient = HttpClients.createDefault();
@BeforeEach
void setUp() {
}
@Test
void shouldReturnProxyHttpClient() {
doReturn(proxyHttpClient).when(smartDocumentsConfiguration).createProxyHttpClient();
when(properties.getProxy()).thenReturn(new ProxyConfiguration());
var httpClient = smartDocumentsConfiguration.buildHttpClient();
assertThat(httpClient).isSameAs(proxyHttpClient);
}
@Test
void shouldReturnNonProxyHttpClient() {
doReturn(nonProxyHttpClient).when(smartDocumentsConfiguration).createNoProxyHttpClient();
when(properties.getProxy()).thenReturn(null);
var httpClient = smartDocumentsConfiguration.buildHttpClient();
assertThat(httpClient).isSameAs(nonProxyHttpClient);
}
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment