From 02716fe8b05873abc58329ac357e86912b77e13f Mon Sep 17 00:00:00 2001 From: Martin Vogel <martin.vogel@sinc.de> Date: Tue, 14 Jun 2022 08:55:40 +0200 Subject: [PATCH] #414 Use WebClient to enable future async http calls --- .../java/fitconnect/api/package-info.java | 11 +++ .../de/fitconnect/client/ClientFactory.java | 6 +- ...itConnectClient.java => SenderClient.java} | 18 ++--- .../java/de/fitconnect/client/TestRunner.java | 2 +- impl/pom.xml | 4 ++ .../fitconnect/impl/SubmissionSender.java | 4 +- .../fitconnect/impl/SubmissionSubscriber.java | 4 +- .../impl/auth/FitCoAuthService.java | 70 ------------------- .../impl/auth/MonoPublisherOAuthService.java | 68 ++++++++++++++++++ .../impl/auth/OAuthTokenIntegrationTest.java | 6 +- pom.xml | 5 ++ 11 files changed, 107 insertions(+), 91 deletions(-) create mode 100644 api/src/main/java/fitconnect/api/package-info.java rename client/src/main/java/de/fitconnect/client/{FitConnectClient.java => SenderClient.java} (75%) delete mode 100644 impl/src/main/java/fitconnect/impl/auth/FitCoAuthService.java create mode 100644 impl/src/main/java/fitconnect/impl/auth/MonoPublisherOAuthService.java diff --git a/api/src/main/java/fitconnect/api/package-info.java b/api/src/main/java/fitconnect/api/package-info.java new file mode 100644 index 000000000..3d1d7dbf9 --- /dev/null +++ b/api/src/main/java/fitconnect/api/package-info.java @@ -0,0 +1,11 @@ +/** + * This module is providing the internal sdk api + * <p> + * + * </p> + * + * @since 1.0 + * @author FitConnect + * @version 1.0 + */ +package fitconnect.api; \ No newline at end of file diff --git a/client/src/main/java/de/fitconnect/client/ClientFactory.java b/client/src/main/java/de/fitconnect/client/ClientFactory.java index 1bf87706b..428157db8 100644 --- a/client/src/main/java/de/fitconnect/client/ClientFactory.java +++ b/client/src/main/java/de/fitconnect/client/ClientFactory.java @@ -8,7 +8,7 @@ import fitconnect.api.services.validation.CertificateValidator; import fitconnect.api.services.validation.MetadataValidator; import fitconnect.impl.SubmissionSender; import fitconnect.impl.SubmissionSubscriber; -import fitconnect.impl.auth.FitCoAuthService; +import fitconnect.impl.auth.MonoPublisherOAuthService; import fitconnect.impl.crypto.JWECryptoService; import fitconnect.impl.validation.KeyValidator; import fitconnect.impl.validation.MetadataSubmissionValidator; @@ -38,7 +38,7 @@ public class ClientFactory { return new JWECryptoService(); } - private static FitCoAuthService getAuthService() { - return new FitCoAuthService(HttpClient.newHttpClient(), TOKEN_URL); + private static MonoPublisherOAuthService getAuthService() { + return new MonoPublisherOAuthService(TOKEN_URL); } } diff --git a/client/src/main/java/de/fitconnect/client/FitConnectClient.java b/client/src/main/java/de/fitconnect/client/SenderClient.java similarity index 75% rename from client/src/main/java/de/fitconnect/client/FitConnectClient.java rename to client/src/main/java/de/fitconnect/client/SenderClient.java index 9ff95f54f..015319557 100644 --- a/client/src/main/java/de/fitconnect/client/FitConnectClient.java +++ b/client/src/main/java/de/fitconnect/client/SenderClient.java @@ -13,30 +13,30 @@ import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; -public class FitConnectClient { +public class SenderClient { - private static final Logger logger = SdkLogger.customLogger(FitConnectClient.class); + private static final Logger logger = SdkLogger.customLogger(SenderClient.class); private final Sender sender; private Optional<OAuthToken> token = Optional.empty(); - public FitConnectClient(){ + public SenderClient(){ this.sender = ClientFactory.submissionSender(); } - public FitConnectClient authenticate(String clientId, String secret, String...scope){ + public SenderClient authenticate(String clientId, String secret, String...scope){ this.token = sender.retrieveOAuthToken(clientId,secret,scope); return this; } - public FitConnectClient createSubmission(final Submission submission){ + public SenderClient createSubmission(final Submission submission){ checkIfAuthenticated(); // TODO implement sendSubmission logger.log(Level.INFO, "created new submission"); return this; } - public FitConnectClient sendSubmission(final Metadata metadata, final Data data){ + public SenderClient sendSubmission(final Metadata metadata, final Data data){ checkIfAuthenticated(); // TODO implement logger.log(Level.INFO, "successfully sent submission"); @@ -44,14 +44,14 @@ public class FitConnectClient { return this; } - public FitConnectClient uploadAttachments(final List<Attachment> attachments){ + public SenderClient uploadAttachments(final List<Attachment> attachments){ checkIfAuthenticated(); // TODO implement logger.log(Level.INFO, "uploaded " + attachments.size() + " attachments"); return this; } - public FitConnectClient printToken(){ + public SenderClient printToken(){ checkIfAuthenticated(); logger.log(Level.INFO, "retrieved access token: " + this.token.get().getAccessToken()); return this; @@ -59,7 +59,7 @@ public class FitConnectClient { private void checkIfAuthenticated() { if(token.isEmpty()){ - throw new IllegalStateException("not authenticated, please authenticate first with authenticate(client, secret)"); + throw new IllegalStateException("not authenticated, please authenticate first"); } } diff --git a/client/src/main/java/de/fitconnect/client/TestRunner.java b/client/src/main/java/de/fitconnect/client/TestRunner.java index 8d9f175b0..2f95ebec3 100644 --- a/client/src/main/java/de/fitconnect/client/TestRunner.java +++ b/client/src/main/java/de/fitconnect/client/TestRunner.java @@ -13,7 +13,7 @@ public class TestRunner { var clientId = "781f6213-0f0f-4a79-9372-e7187ffda98b"; var secret = "PnzR8Vbmhpv_VwTkT34wponqXWK8WBm-LADlryYdV4o"; - FitConnectClient client = new FitConnectClient(); + SenderClient client = new SenderClient(); // sample high -level- api calls to send a submission client.authenticate(clientId,secret) diff --git a/impl/pom.xml b/impl/pom.xml index def6e1125..3608d6846 100644 --- a/impl/pom.xml +++ b/impl/pom.xml @@ -32,6 +32,10 @@ <groupId>com.github.erosb</groupId> <artifactId>everit-json-schema</artifactId> </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-webflux</artifactId> + </dependency> </dependencies> diff --git a/impl/src/main/java/fitconnect/impl/SubmissionSender.java b/impl/src/main/java/fitconnect/impl/SubmissionSender.java index dc441e79c..ecbb4ff54 100644 --- a/impl/src/main/java/fitconnect/impl/SubmissionSender.java +++ b/impl/src/main/java/fitconnect/impl/SubmissionSender.java @@ -13,7 +13,7 @@ import fitconnect.api.exceptions.EncryptionException; import fitconnect.impl.logger.SdkLogger; import fitconnect.api.services.validation.CertificateValidator; import fitconnect.api.domain.validation.ValidationResult; -import fitconnect.impl.auth.FitCoAuthService; +import fitconnect.impl.auth.MonoPublisherOAuthService; import java.nio.charset.StandardCharsets; import java.util.List; @@ -24,7 +24,7 @@ import java.util.logging.Logger; public class SubmissionSender implements Sender { - private static final Logger logger = SdkLogger.defaultLogger(FitCoAuthService.class); + private static final Logger logger = SdkLogger.defaultLogger(MonoPublisherOAuthService.class); private final OAuthService authService; private final CertificateValidator certificateValidator; diff --git a/impl/src/main/java/fitconnect/impl/SubmissionSubscriber.java b/impl/src/main/java/fitconnect/impl/SubmissionSubscriber.java index 4e0c68c33..0159508a4 100644 --- a/impl/src/main/java/fitconnect/impl/SubmissionSubscriber.java +++ b/impl/src/main/java/fitconnect/impl/SubmissionSubscriber.java @@ -13,7 +13,7 @@ import fitconnect.api.exceptions.DecryptionException; import fitconnect.impl.logger.SdkLogger; import fitconnect.api.services.validation.MetadataValidator; import fitconnect.api.domain.validation.ValidationResult; -import fitconnect.impl.auth.FitCoAuthService; +import fitconnect.impl.auth.MonoPublisherOAuthService; import java.util.Optional; import java.util.logging.Level; @@ -22,7 +22,7 @@ import java.util.logging.Logger; public class SubmissionSubscriber implements Subscriber { - private static final Logger logger = SdkLogger.defaultLogger(FitCoAuthService.class); + private static final Logger logger = SdkLogger.defaultLogger(MonoPublisherOAuthService.class); private final OAuthService authService; private final CryptoService cryptoService; diff --git a/impl/src/main/java/fitconnect/impl/auth/FitCoAuthService.java b/impl/src/main/java/fitconnect/impl/auth/FitCoAuthService.java deleted file mode 100644 index 4b73d3d46..000000000 --- a/impl/src/main/java/fitconnect/impl/auth/FitCoAuthService.java +++ /dev/null @@ -1,70 +0,0 @@ -package fitconnect.impl.auth; - -import com.fasterxml.jackson.databind.ObjectMapper; -import fitconnect.api.services.auth.OAuthService; -import fitconnect.api.domain.auth.OAuthToken; -import fitconnect.api.exceptions.AuthenticationException; -import fitconnect.impl.logger.SdkLogger; - -import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.util.Arrays; -import java.util.HashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static java.util.stream.Collectors.joining; - -public class FitCoAuthService implements OAuthService { - - private static final Logger logger = SdkLogger.defaultLogger(FitCoAuthService.class); - private static final ObjectMapper mapper = new ObjectMapper(); - - private final HttpClient client; - private final String tokenUrl; - - public FitCoAuthService(final HttpClient client, final String tokenUrl) { - this.client = client; - this.tokenUrl = tokenUrl; - } - - @Override - public OAuthToken authenticate(String clientId, String clientSecret, String... scope) throws AuthenticationException { - final String requestBody = buildRequestBody(clientId, clientSecret, scope); - try { - return performTokenRequest(requestBody); - } catch (IOException | InterruptedException e) { - logger.log(Level.SEVERE, "error retrieving access token", e); - throw new AuthenticationException(e.getMessage(),e); - } - } - - private String buildRequestBody(String clientId, String clientSecret, String... scope) { - var data = new HashMap<String, String>() {{ - put("grant_type", "client_credentials"); - put("client_id", clientId); - put("client_secret", clientSecret); - }}; - - Arrays.stream(scope).forEach(s -> data.put("scope", s)); - - return data.entrySet() - .stream() - .map(e -> e.getKey() + "=" + e.getValue()) - .collect(joining("&")); - } - - private OAuthToken performTokenRequest(final String requestBody) throws IOException, InterruptedException { - HttpRequest request = HttpRequest.newBuilder() - .header("Accept", "application/json") - .header("Content-Type","application/x-www-form-urlencoded;charset=UTF-8") - .uri(URI.create(tokenUrl)) - .POST(HttpRequest.BodyPublishers.ofString(requestBody)) - .build(); - var responseBody = client.send(request, HttpResponse.BodyHandlers.ofString()).body(); - return mapper.readValue(responseBody,OAuthToken.class); - } -} diff --git a/impl/src/main/java/fitconnect/impl/auth/MonoPublisherOAuthService.java b/impl/src/main/java/fitconnect/impl/auth/MonoPublisherOAuthService.java new file mode 100644 index 000000000..3981b148a --- /dev/null +++ b/impl/src/main/java/fitconnect/impl/auth/MonoPublisherOAuthService.java @@ -0,0 +1,68 @@ +package fitconnect.impl.auth; + +import fitconnect.api.domain.auth.OAuthToken; +import fitconnect.api.exceptions.AuthenticationException; +import fitconnect.api.services.auth.OAuthService; +import fitconnect.impl.logger.SdkLogger; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.logging.Logger; + +import static java.util.stream.Collectors.joining; + +public class MonoPublisherOAuthService implements OAuthService { + + private static final Logger logger = SdkLogger.defaultLogger(MonoPublisherOAuthService.class); + + private final String tokenUrl; + + public MonoPublisherOAuthService(final String tokenUrl) { + this.tokenUrl = tokenUrl; + } + + @Override + public OAuthToken authenticate(String clientId, String clientSecret, String... scope) throws AuthenticationException { + final String requestBody = buildRequestBody(clientId, clientSecret, scope); + + return performTokenRequestAsync(requestBody).block(); + } + + private String buildRequestBody(String clientId, String clientSecret, String... scope) { + var data = new HashMap<String, String>() {{ + put("grant_type", "client_credentials"); + put("client_id", clientId); + put("client_secret", clientSecret); + }}; + + Arrays.stream(scope).forEach(s -> data.put("scope", s)); + + return data.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).collect(joining("&")); + } + + // TODO generalize HTTP requests in separate common service + private Mono<OAuthToken> performTokenRequestAsync(final String requestBody){ + return WebClient.builder() + .defaultHeaders(this::setHeaders) + .build() + .post() + .uri(tokenUrl) + .bodyValue(requestBody) + .retrieve() + .bodyToMono(OAuthToken.class); + } + + private void setHeaders(HttpHeaders headers) { + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.setAcceptCharset(List.of(StandardCharsets.UTF_8)); + } + +} diff --git a/impl/src/test/java/fitconnect/impl/auth/OAuthTokenIntegrationTest.java b/impl/src/test/java/fitconnect/impl/auth/OAuthTokenIntegrationTest.java index e282aff6b..2d82a9172 100644 --- a/impl/src/test/java/fitconnect/impl/auth/OAuthTokenIntegrationTest.java +++ b/impl/src/test/java/fitconnect/impl/auth/OAuthTokenIntegrationTest.java @@ -1,11 +1,10 @@ package fitconnect.impl.auth; -import fitconnect.api.services.Sender; import fitconnect.api.domain.auth.OAuthToken; +import fitconnect.api.services.Sender; import fitconnect.impl.SubmissionSender; import org.junit.jupiter.api.Test; -import java.net.http.HttpClient; import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; @@ -21,8 +20,7 @@ class OAuthTokenIntegrationTest { var secret = "PnzR8Vbmhpv_VwTkT34wponqXWK8WBm-LADlryYdV4o"; var scope = "send:region:DE"; - var httpClient = HttpClient.newHttpClient(); - var authService = new FitCoAuthService(httpClient, tokenUrl); + var authService = new MonoPublisherOAuthService(tokenUrl); final Sender sender = new SubmissionSender(authService, null, null); // When diff --git a/pom.xml b/pom.xml index 0981ba0ec..ead23af03 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,11 @@ <artifactId>everit-json-schema</artifactId> <version>1.14.1</version> </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-webflux</artifactId> + <version>2.7.0</version> + </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> -- GitLab