Skip to content
Snippets Groups Projects
Commit 6cff9304 authored by Henry Borasch's avatar Henry Borasch
Browse files

978: removed spring-web

- removed dependency
- added OkHttpClient
- created intermediate client to abstract between logic and HTTP handling
- adjusted tests
parent 2b312996
No related branches found
No related tags found
1 merge request!169978: remove spring-web
This commit is part of merge request !169. Comments created here will be created in the context of that merge request.
Showing
with 425 additions and 388 deletions
......@@ -30,10 +30,6 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
<build>
......
......@@ -7,14 +7,14 @@ final class ResourcePaths {
static final String AUTH_TOKEN_PATH = "/token";
static final String DESTINATIONS_PATH = "/v1/destinations/{destinationId}";
static final String DESTINATIONS_KEY_PATH = "/v1/destinations/{destinationId}/keys/{kid}";
static final String DESTINATIONS_PATH = "/v1/destinations/%s";
static final String DESTINATIONS_KEY_PATH = "/v1/destinations/%s/keys/%s";
static final String EVENTS_PATH = "/v1/cases/{caseId}/events";
static final String EVENTS_PATH = "/v1/cases/%s/events";
static final String SUBMISSION_PATH = "/v1/submissions/{submissionId}";
static final String SUBMISSION_PATH = "/v1/submissions/%s";
static final String SUBMISSIONS_PATH = "/v1/submissions";
static final String SUBMISSION_ATTACHMENT_PATH = "/v1/submissions/{submissionId}/attachments/{attachmentId}";
static final String SUBMISSION_ATTACHMENT_PATH = "/v1/submissions/%s/attachments/%s";
static final String ROUTING_AREA_PATH = "/v1/areas";
static final String ROUTING_ROUTE_PATH = "/v1/routes";
......
......@@ -2,11 +2,30 @@ package dev.fitko.fitconnect.api.exceptions;
public class RestApiException extends RuntimeException {
private int statusCode = 400;
public RestApiException(final String errorMessage, final Throwable error) {
super(errorMessage, error);
}
public RestApiException(final String errorMessage, final Throwable error, final int statusCode) {
super(errorMessage, error);
this.statusCode = statusCode;
}
public RestApiException(final String errorMessage) {
super(errorMessage);
}
public RestApiException(final String errorMessage, int statusCode) {
super(errorMessage);
}
public int getStatusCode() {
return statusCode;
}
public boolean isNotFound() {
return this.statusCode == 404;
}
}
......@@ -34,6 +34,7 @@ import dev.fitko.fitconnect.core.crypto.JWECryptoService;
import dev.fitko.fitconnect.core.events.EventLogApiService;
import dev.fitko.fitconnect.core.events.EventLogVerifier;
import dev.fitko.fitconnect.core.events.SecurityEventTokenService;
import dev.fitko.fitconnect.core.http.HttpClient;
import dev.fitko.fitconnect.core.http.RestService;
import dev.fitko.fitconnect.core.keys.PublicKeyService;
import dev.fitko.fitconnect.core.routing.RouteVerifier;
......@@ -44,7 +45,6 @@ import dev.fitko.fitconnect.core.util.CertificateLoader;
import dev.fitko.fitconnect.core.validation.DefaultValidationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;
import org.yaml.snakeyaml.Yaml;
import java.io.IOException;
......@@ -102,6 +102,7 @@ public final class ClientFactory {
return new SubscriberClient(subscriber, submissionReceiver);
}
/**
* Create a new {@link RoutingClient} to find destinations and services that is automatically configured via a provided {@link ApplicationConfig}.
*
......@@ -112,74 +113,74 @@ public final class ClientFactory {
checkNotNull(config);
LOGGER.info("Initializing routing client ...");
final RestTemplate restTemplate = getRestTemplate(config, loadBuildInfo());
final SchemaProvider schemaProvider = getSchemaProvider(restTemplate, config.getSubmissionDataSchemaPath());
final OAuthService authService = getSenderConfiguredAuthService(config, restTemplate);
final HttpClient httpClient = getHttpClient(config, loadBuildInfo());
final SchemaProvider schemaProvider = getSchemaProvider(httpClient, config.getSubmissionDataSchemaPath());
final OAuthService authService = getSenderConfiguredAuthService(config, httpClient);
final MessageDigestService messageDigestService = getMessageDigestService();
final ValidationService validator = getValidationService(config, schemaProvider, messageDigestService);
final SubmissionService submissionService = getSubmissionService(config, restTemplate, authService);
final KeyService keyService = getKeyService(config, restTemplate, authService, submissionService, validator);
final SubmissionService submissionService = getSubmissionService(config, httpClient, authService);
final KeyService keyService = getKeyService(config, httpClient, authService, submissionService, validator);
final RouteVerifier routeVerifier = getRouteVerifier(keyService, validator);
final RoutingService routingService = getRoutingService(config, restTemplate);
final RoutingService routingService = getRoutingService(config, httpClient);
return new RoutingClient(routingService, routeVerifier);
}
private static Sender getSender(final ApplicationConfig config, final BuildInfo buildInfo) {
final RestTemplate restTemplate = getRestTemplate(config, buildInfo);
final SchemaProvider schemaProvider = getSchemaProvider(restTemplate, config.getSubmissionDataSchemaPath());
final HttpClient httpClient = getHttpClient(config, buildInfo);
final SchemaProvider schemaProvider = getSchemaProvider(httpClient, config.getSubmissionDataSchemaPath());
final MessageDigestService messageDigestService = getMessageDigestService();
final CryptoService cryptoService = getCryptoService(messageDigestService);
final ValidationService validator = getValidationService(config, schemaProvider, messageDigestService);
final OAuthService authService = getSenderConfiguredAuthService(config, restTemplate);
final OAuthService authService = getSenderConfiguredAuthService(config, httpClient);
final SubmissionService submissionService = getSubmissionService(config, restTemplate, authService);
final KeyService keyService = getKeyService(config, restTemplate, authService, submissionService, validator);
final SubmissionService submissionService = getSubmissionService(config, httpClient, authService);
final KeyService keyService = getKeyService(config, httpClient, authService, submissionService, validator);
final EventLogVerificationService eventLogVerifier = getEventLogVerifier(keyService, validator);
final EventLogService eventLogService = getEventLogService(config, restTemplate, eventLogVerifier, authService);
final EventLogService eventLogService = getEventLogService(config, httpClient, eventLogVerifier, authService);
return new SubmissionSender(submissionService, eventLogService, cryptoService, validator, keyService);
}
private static Subscriber getSubscriber(final ApplicationConfig config, final BuildInfo buildInfo) {
final RestTemplate restTemplate = getRestTemplate(config, buildInfo);
final SchemaProvider schemaProvider = getSchemaProvider(restTemplate, config.getSubmissionDataSchemaPath());
final HttpClient httpClient = getHttpClient(config, buildInfo);
final SchemaProvider schemaProvider = getSchemaProvider(httpClient, config.getSubmissionDataSchemaPath());
final MessageDigestService messageDigestService = getMessageDigestService();
final CryptoService cryptoService = getCryptoService(messageDigestService);
final ValidationService validator = getValidationService(config, schemaProvider, messageDigestService);
final OAuthService authService = getSubscriberConfiguredAuthService(config, restTemplate);
final OAuthService authService = getSubscriberConfiguredAuthService(config, httpClient);
final SubmissionService submissionService = getSubmissionService(config, restTemplate, authService);
final KeyService keyService = getKeyService(config, restTemplate, authService, submissionService, validator);
final SubmissionService submissionService = getSubmissionService(config, httpClient, authService);
final KeyService keyService = getKeyService(config, httpClient, authService, submissionService, validator);
final EventLogVerificationService eventLogVerifier = getEventLogVerifier(keyService, validator);
final EventLogService eventLogService = getEventLogService(config, restTemplate, eventLogVerifier, authService);
final EventLogService eventLogService = getEventLogService(config, httpClient, eventLogVerifier, authService);
final SecurityEventService setService = getSecurityEventTokenService(config, validator);
return new SubmissionSubscriber(submissionService, eventLogService, cryptoService, validator, setService);
}
private static OAuthService getSenderConfiguredAuthService(final ApplicationConfig config, final RestTemplate restTemplate) {
private static OAuthService getSenderConfiguredAuthService(final ApplicationConfig config, final HttpClient httpClient) {
final String clientId = config.getSenderConfig().getClientId();
final String clientSecret = config.getSenderConfig().getClientSecret();
return new DefaultOAuthService(restTemplate, clientId, clientSecret, config.getOAuthTokenEndpoint());
return new DefaultOAuthService(httpClient, clientId, clientSecret, config.getOAuthTokenEndpoint());
}
private static OAuthService getSubscriberConfiguredAuthService(final ApplicationConfig config, final RestTemplate restTemplate) {
private static OAuthService getSubscriberConfiguredAuthService(final ApplicationConfig config, final HttpClient httpClient) {
final String clientId = config.getSubscriberConfig().getClientId();
final String clientSecret = config.getSubscriberConfig().getClientSecret();
return new DefaultOAuthService(restTemplate, clientId, clientSecret, config.getOAuthTokenEndpoint());
return new DefaultOAuthService(httpClient, clientId, clientSecret, config.getOAuthTokenEndpoint());
}
private static SubmissionService getSubmissionService(final ApplicationConfig config, final RestTemplate restTemplate, final OAuthService authService) {
return new SubmissionApiService(authService, restTemplate, config);
private static SubmissionService getSubmissionService(final ApplicationConfig config, final HttpClient httpClient, final OAuthService authService) {
return new SubmissionApiService(authService, httpClient, config);
}
private static EventLogService getEventLogService(final ApplicationConfig config, final RestTemplate restTemplate, final EventLogVerificationService eventLogVerifier, final OAuthService authService) {
return new EventLogApiService(config, authService, restTemplate, eventLogVerifier);
private static EventLogService getEventLogService(final ApplicationConfig config, final HttpClient httpClient, final EventLogVerificationService eventLogVerifier, final OAuthService authService) {
return new EventLogApiService(config, authService, httpClient, eventLogVerifier);
}
private static ValidationService getValidationService(final ApplicationConfig config, final SchemaProvider schemaProvider, final MessageDigestService messageDigestService) {
......@@ -190,9 +191,9 @@ public final class ClientFactory {
return new JWECryptoService(messageDigestService);
}
private static RestTemplate getRestTemplate(final ApplicationConfig config, final BuildInfo buildInfo) {
private static HttpClient getHttpClient(final ApplicationConfig config, final BuildInfo buildInfo) {
final RestService restService = new RestService(config.getHttpProxyHost(), config.getHttpProxyPort(), buildInfo);
return restService.getRestTemplate();
return restService.getHttpClient();
}
private static MessageDigestService getMessageDigestService() {
......@@ -206,24 +207,24 @@ public final class ClientFactory {
return new SecurityEventTokenService(config, validationService, rsaKey);
}
private static KeyService getKeyService(final ApplicationConfig config, final RestTemplate restTemplate, final OAuthService authService, final SubmissionService submissionService, final ValidationService validator) {
return new PublicKeyService(config, restTemplate, authService, submissionService, validator);
private static KeyService getKeyService(final ApplicationConfig config, final HttpClient httpClient, final OAuthService authService, final SubmissionService submissionService, final ValidationService validator) {
return new PublicKeyService(config, httpClient, authService, submissionService, validator);
}
private static EventLogVerificationService getEventLogVerifier(final KeyService keyService, final ValidationService validationService) {
return new EventLogVerifier(keyService, validationService);
}
private static SchemaProvider getSchemaProvider(RestTemplate restTemplate, String submissionDataSchemaPath) {
private static SchemaProvider getSchemaProvider(HttpClient httpClient, String submissionDataSchemaPath) {
final List<String> setSchemaFiles = SchemaConfig.getSetSchemaFilePaths(SET_SCHEMA_DIR);
final List<String> metadataSchemaFiles = SchemaConfig.getMetadataSchemaFileNames(METADATA_SCHEMA_DIR);
final List<String> destinationSchemaFiles = SchemaConfig.getDestinationSchemaPaths(DESTINATION_SCHEMA_DIR);
final SchemaResources schemaResources = new SchemaResources(setSchemaFiles, metadataSchemaFiles, destinationSchemaFiles, submissionDataSchemaPath);
return new SchemaResourceProvider(restTemplate, schemaResources);
return new SchemaResourceProvider(httpClient, schemaResources);
}
private static RoutingService getRoutingService(final ApplicationConfig config, final RestTemplate restTemplate) {
return new RoutingApiService(config, restTemplate);
private static RoutingService getRoutingService(final ApplicationConfig config, final HttpClient httpClient) {
return new RoutingApiService(config, httpClient);
}
private static RouteVerifier getRouteVerifier(final KeyService keyService, final ValidationService validationService) {
......@@ -254,13 +255,13 @@ public final class ClientFactory {
}
private static void checkNotNull(final ApplicationConfig config) {
if(config == null){
if (config == null) {
throw new InitializationException("application config must not be null");
}
}
private static String getPrivateDecryptionKeyPathFromSubscriber(final SubscriberConfig subscriberConfig) {
if(subscriberConfig.getPrivateDecryptionKeyPaths().size() != 1){
if (subscriberConfig.getPrivateDecryptionKeyPaths().size() != 1) {
throw new InitializationException("Currently only one configured private key per subscriber is allowed !");
}
final String keyPath = subscriberConfig.getPrivateDecryptionKeyPaths().get(0);
......
......@@ -34,7 +34,6 @@ import dev.fitko.fitconnect.client.subscriber.model.ReceivedData;
import dev.fitko.fitconnect.core.util.StopWatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.HttpClientErrorException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
......@@ -193,7 +192,7 @@ public class SubmissionReceiver {
LOGGER.info("Downloading attachment {} took {}", metadata.getAttachmentId(), StopWatch.stopWithFormattedTime(startDownload));
return encryptedAttachment;
} catch (final RestApiException e) {
if (e.getCause() instanceof HttpClientErrorException.NotFound) {
if (e.isNotFound()) {
rejectSubmissionWithProblem(submission, new MissingAttachment(metadata.getAttachmentId()));
}
throw new SubmissionRequestException(e.getMessage(), e);
......
......@@ -43,8 +43,6 @@ import dev.fitko.fitconnect.core.crypto.JWECryptoService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.HttpStatus;
import org.springframework.web.client.HttpClientErrorException;
import java.io.File;
import java.io.IOException;
......@@ -62,7 +60,10 @@ import java.util.UUID;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.anyLong;
......@@ -730,7 +731,7 @@ class SubscriberClientTest {
when(subscriberMock.decryptStringContent(encryptionKey, submission.getEncryptedData())).thenReturn(dataPayload.getBytes());
when(subscriberMock.decryptStringContent(encryptionKey, submission.getEncryptedMetadata())).thenReturn(metadataBytes);
when(subscriberMock.getAuthenticationTagsForEvent(any(), any())).thenReturn(authenticationTags);
when(subscriberMock.fetchAttachment(any(), any())).thenThrow(new RestApiException("Attachment download failed", HttpClientErrorException.create(HttpStatus.NOT_FOUND, "attachment not found", null, null, null)));
when(subscriberMock.fetchAttachment(any(), any())).thenThrow(new RestApiException("Attachment download failed", 404));
// When
final SubmissionRequestException exception = assertThrows(SubmissionRequestException.class, () -> underTest.requestSubmission(submissionId));
......
......@@ -30,10 +30,6 @@
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
......@@ -51,6 +47,11 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.10.0</version>
</dependency>
<!-- Tests -->
<dependency>
......
......@@ -4,28 +4,28 @@ import dev.fitko.fitconnect.api.domain.auth.OAuthToken;
import dev.fitko.fitconnect.api.exceptions.AuthenticationException;
import dev.fitko.fitconnect.api.exceptions.RestApiException;
import dev.fitko.fitconnect.api.services.auth.OAuthService;
import dev.fitko.fitconnect.core.http.HttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static dev.fitko.fitconnect.core.http.HttpHeaders.ACCEPT;
import static dev.fitko.fitconnect.core.http.HttpHeaders.ACCEPT_CHARSET;
import static dev.fitko.fitconnect.core.http.HttpHeaders.CONTENT_TYPE;
import static dev.fitko.fitconnect.core.http.MimeTypes.APPLICATION_FORM_URLENCODED;
import static dev.fitko.fitconnect.core.http.MimeTypes.APPLICATION_JSON;
import static java.util.stream.Collectors.joining;
public class DefaultOAuthService implements OAuthService {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultOAuthService.class);
private final RestTemplate restTemplate;
private final HttpClient httpClient;
private final String tokenUrl;
private final String clientId;
......@@ -34,8 +34,8 @@ public class DefaultOAuthService implements OAuthService {
private OAuthToken currentToken;
private LocalDateTime tokenExpirationTime;
public DefaultOAuthService(final RestTemplate restTemplate, final String clientId, final String clientSecret, final String authUrl) {
this.restTemplate = restTemplate;
public DefaultOAuthService(final HttpClient httpClient, final String clientId, final String clientSecret, final String authUrl) {
this.httpClient = httpClient;
this.clientId = clientId;
this.clientSecret = clientSecret;
tokenUrl = authUrl;
......@@ -91,23 +91,21 @@ public class DefaultOAuthService implements OAuthService {
}
private OAuthToken performTokenRequest(final String requestBody) throws RestApiException {
final HttpHeaders headers = getHeaders();
final HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);
try {
LOGGER.info("Sending authentication request");
return restTemplate.exchange(tokenUrl, HttpMethod.POST, entity, OAuthToken.class).getBody();
} catch (final RestClientException e) {
return this.httpClient.post(tokenUrl, getHeaders(), requestBody, OAuthToken.class).getBody();
} catch (final IOException e) {
LOGGER.error(e.getMessage(), e);
throw new RestApiException("Could not retrieve OAuth token", e);
}
}
private HttpHeaders getHeaders() {
final var headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.setAcceptCharset(List.of(StandardCharsets.UTF_8));
return headers;
}
private Map<String, String> getHeaders() {
return new HashMap<>(Map.of(
ACCEPT, APPLICATION_JSON,
CONTENT_TYPE, APPLICATION_FORM_URLENCODED,
ACCEPT_CHARSET, StandardCharsets.UTF_8.toString()));
}
}
......@@ -17,19 +17,19 @@ import dev.fitko.fitconnect.api.exceptions.SubmitEventNotFoundException;
import dev.fitko.fitconnect.api.services.auth.OAuthService;
import dev.fitko.fitconnect.api.services.events.EventLogService;
import dev.fitko.fitconnect.api.services.events.EventLogVerificationService;
import dev.fitko.fitconnect.core.http.HttpClient;
import dev.fitko.fitconnect.core.http.HttpHeaders;
import dev.fitko.fitconnect.core.http.MimeTypes;
import dev.fitko.fitconnect.core.util.EventLogUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
......@@ -43,20 +43,19 @@ import static dev.fitko.fitconnect.core.util.EventLogUtil.mapEventLogToEntries;
public class EventLogApiService implements EventLogService {
private static final Logger LOGGER = LoggerFactory.getLogger(EventLogApiService.class);
public static final MediaType MEDIA_TYPE_JOSE = MediaType.parseMediaType("application/jose");
private final ApplicationConfig config;
private final OAuthService authService;
private final RestTemplate restTemplate;
private final HttpClient httpClient;
private final EventLogVerificationService eventLogVerifier;
public EventLogApiService(final ApplicationConfig config,
final OAuthService authService,
final RestTemplate restTemplate,
final HttpClient httpClient,
final EventLogVerificationService eventLogVerifier) {
this.config = config;
this.authService = authService;
this.restTemplate = restTemplate;
this.httpClient = httpClient;
this.eventLogVerifier = eventLogVerifier;
}
......@@ -111,33 +110,29 @@ public class EventLogApiService implements EventLogService {
@Override
public void sendEvent(final UUID caseId, final String signedAndSerializedSET) {
final String url = config.getEventsEndpoint();
final HttpHeaders headers = getHttpHeaders(MEDIA_TYPE_JOSE);
final HttpEntity<String> entity = new HttpEntity<>(signedAndSerializedSET, headers);
final String url = String.format(config.getEventsEndpoint(), caseId);
try {
restTemplate.exchange(url, HttpMethod.POST, entity, Void.class, caseId);
} catch (final RestClientException e) {
this.httpClient.post(url, getHttpHeaders(MimeTypes.APPLICATION_JOSE), signedAndSerializedSET, Void.class);
} catch (final IOException e) {
throw new RestApiException("Sending event failed", e);
}
}
private EventLog loadEventLog(final UUID caseId) {
final String url = config.getEventsEndpoint();
final HttpHeaders headers = getHttpHeaders(MediaType.APPLICATION_JSON);
final HttpEntity<String> entity = new HttpEntity<>(headers);
final String url = String.format(config.getEventsEndpoint(), caseId);
try {
return restTemplate.exchange(url, HttpMethod.GET, entity, EventLog.class, caseId).getBody();
} catch (final RestClientException e) {
return this.httpClient.get(url, getHttpHeaders(MimeTypes.APPLICATION_JSON), EventLog.class).getBody();
} catch (final IOException e) {
throw new EventLogException("EventLog query failed", e);
}
}
private HttpHeaders getHttpHeaders(final MediaType mediaType) {
final var headers = new HttpHeaders();
headers.setBearerAuth(authService.getCurrentToken().getAccessToken());
headers.setContentType(mediaType);
headers.setAcceptCharset(List.of(StandardCharsets.UTF_8));
return headers;
private Map<String, String> getHttpHeaders(final String mediaType) {
return new HashMap<>(Map.of(
HttpHeaders.AUTHORIZATION, "Bearer " + authService.getCurrentToken().getAccessToken(),
HttpHeaders.CONTENT_TYPE, mediaType,
HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.toString()));
}
private SubmissionStatus getVerifiedStatus(final ValidationContext validationContext, final SignedJWT latestEvent) {
......
package dev.fitko.fitconnect.core.http;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
/**
* Intercepts HTTP calls and logs the request url
*/
public class ApiRequestInterceptor implements ClientHttpRequestInterceptor {
public class ApiRequestInterceptor implements Interceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(ApiRequestInterceptor.class);
@NotNull
@Override
public ClientHttpResponse intercept(final HttpRequest req, final byte[] reqBody,
final ClientHttpRequestExecution ex) throws IOException {
LOGGER.info("{}", req.getURI());
return ex.execute(req, reqBody);
public Response intercept(@NotNull Chain chain) throws IOException {
Request request = chain.request();
LOGGER.info("{}", request.url());
return chain.proceed(request);
}
}
package dev.fitko.fitconnect.core.http;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.fitko.fitconnect.api.exceptions.RestApiException;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import java.io.IOException;
import java.net.Proxy;
import java.util.List;
import java.util.Map;
public class HttpClient {
private final OkHttpClient httpClient;
private final ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
private final boolean throwExceptionsOnFailure;
public HttpClient(boolean throwExceptionsOnFailure) {
this.httpClient = new OkHttpClient();
this.throwExceptionsOnFailure = throwExceptionsOnFailure;
}
public HttpClient(boolean throwExceptionsOnFailure, List<Interceptor> interceptors) {
this.httpClient = builderWithInterceptors(interceptors).build();
this.throwExceptionsOnFailure = throwExceptionsOnFailure;
}
public HttpClient(boolean throwExceptionsOnFailure, List<Interceptor> interceptors, Proxy proxy) {
this.httpClient = builderWithInterceptors(interceptors).proxy(proxy).build();
this.throwExceptionsOnFailure = throwExceptionsOnFailure;
}
private OkHttpClient.Builder builderWithInterceptors(List<Interceptor> interceptors) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
interceptors.forEach(builder::addInterceptor);
return builder;
}
public <R> HttpResponse<R> get(String url, Class<R> responseType) throws IOException {
return get(url, Map.of(), responseType);
}
public <R> HttpResponse<R> get(String url, Map<String, String> headers, Class<R> responseType) throws IOException {
Request request = new Request.Builder()
.url(url)
.get()
.headers(Headers.of(headers))
.build();
try (Response response = this.httpClient.newCall(request).execute()) {
return this.evaluateStatusAndRespond(response, responseType);
}
}
public <P, R> HttpResponse<R> post(String url, P httpPayload, Class<R> responseType) throws IOException {
return post(url, Map.of(), httpPayload, responseType);
}
public <P, R> HttpResponse<R> post(String url, Map<String, String> headers, P httpPayload, Class<R> responseType) throws IOException {
RequestBody requestBody = RequestBody.create(this.objectMapper.writeValueAsString(httpPayload), MediaType.get(MimeTypes.APPLICATION_JSON));
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.headers(Headers.of(headers))
.build();
try (Response response = this.httpClient.newCall(request).execute()) {
return this.evaluateStatusAndRespond(response, responseType);
}
}
public <P, R> HttpResponse<R> put(String url, P httpPayload, Class<R> responseType) throws IOException {
return put(url, Map.of(), httpPayload, responseType);
}
public <P, R> HttpResponse<R> put(String url, Map<String, String> headers, P httpPayload, Class<R> responseType) throws IOException {
RequestBody requestBody = RequestBody.create(this.objectMapper.writeValueAsString(httpPayload), MediaType.get(MimeTypes.APPLICATION_JSON));
Request request = new Request.Builder()
.url(url)
.put(requestBody)
.headers(Headers.of(headers))
.build();
try (Response response = this.httpClient.newCall(request).execute()) {
return this.evaluateStatusAndRespond(response, responseType);
}
}
private <R> HttpResponse<R> evaluateStatusAndRespond(Response response, Class<R> responseType) throws IOException {
if (this.throwExceptionsOnFailure && !response.isSuccessful()) {
throw new RestApiException("The Request failed.", response.code());
}
ResponseBody responseBody = response.body();
if (responseBody == null) {
throw new IOException("Response body is null.");
}
return new HttpResponse<R>(response.code(), this.objectMapper.readValue(responseBody.string(), responseType));
}
}
package dev.fitko.fitconnect.core.http;
public class HttpHeaders {
public final static String ACCEPT = "Accept";
public final static String ACCEPT_CHARSET = "Accept-Charset";
public final static String AUTHORIZATION = "Authorization";
public final static String CONTENT_TYPE = "Content-Type";
public final static String USER_AGENT = "User-Agent";
}
package dev.fitko.fitconnect.core.http;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class HttpResponse<T> {
private int statusCode;
private T body;
}
package dev.fitko.fitconnect.core.http;
public class MimeTypes {
public final static String APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded";
public final static String APPLICATION_JOSE = "application/jose";
public final static String APPLICATION_JSON = "application/json; charset=utf-8";
public final static String APPLICATION_PROBLEM_JSON = "application/problem+json";
}
package dev.fitko.fitconnect.core.http;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.fitko.fitconnect.api.config.BuildInfo;
import dev.fitko.fitconnect.core.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.List;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
public class RestService {
private static final Logger LOGGER = LoggerFactory.getLogger(RestService.class);
......@@ -34,44 +27,15 @@ public class RestService {
this(null, 0, buildInfo);
}
public RestTemplate getRestTemplate() {
return hasProxySet() ? getProxyRestTemplate() : getDefaultRestTemplate();
}
public HttpClient getHttpClient() {
private RestTemplate getDefaultRestTemplate() {
if (hasProxySet()) {
LOGGER.info("Using proxy {}", this);
return new HttpClient(true, List.of(new ApiRequestInterceptor(), new UserAgentInterceptor(buildInfo)),
new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)));
}
LOGGER.info("No proxy configured");
final RestTemplate restTemplate = new RestTemplate();
setupTemplate(restTemplate);
return restTemplate;
}
private RestTemplate getProxyRestTemplate() {
LOGGER.info("Using proxy {}", this);
final var requestFactory = new SimpleClientHttpRequestFactory();
final var proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
requestFactory.setProxy(proxy);
final RestTemplate proxyRestTemplate = new RestTemplate(requestFactory);
setupTemplate(proxyRestTemplate);
return proxyRestTemplate;
}
private void setMappingConverters(final RestTemplate restTemplate) {
final MappingJackson2HttpMessageConverter jacksonMessageConverter = new MappingJackson2HttpMessageConverter();
jacksonMessageConverter.setObjectMapper(new ObjectMapper().configure(FAIL_ON_UNKNOWN_PROPERTIES, false));
restTemplate.getMessageConverters().add(jacksonMessageConverter);
}
private void setLoggingInterceptors(final RestTemplate restTemplate) {
final List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
interceptors.add(new ApiRequestInterceptor());
interceptors.add(new UserAgentInterceptor(buildInfo));
restTemplate.setInterceptors(interceptors);
}
private void setupTemplate(final RestTemplate restTemplate) {
setLoggingInterceptors(restTemplate);
setMappingConverters(restTemplate);
return new HttpClient(true, List.of(new ApiRequestInterceptor(), new UserAgentInterceptor(buildInfo)));
}
boolean hasProxySet() {
......
package dev.fitko.fitconnect.core.http;
import dev.fitko.fitconnect.api.config.BuildInfo;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
public class UserAgentInterceptor implements ClientHttpRequestInterceptor {
import static dev.fitko.fitconnect.core.http.HttpHeaders.USER_AGENT;
public class UserAgentInterceptor implements Interceptor {
private final String productName;
private final String productVersion;
......@@ -21,13 +22,13 @@ public class UserAgentInterceptor implements ClientHttpRequestInterceptor {
this.commit = buildInfo.getCommit();
}
@NotNull
@Override
public ClientHttpResponse intercept(final HttpRequest req, final byte[] reqBody,
final ClientHttpRequestExecution ex) throws IOException {
public Response intercept(@NotNull Chain chain) throws IOException {
req.getHeaders().set(HttpHeaders.USER_AGENT, String.format("%s/%s (commit:%s;os:%s)",
this.productName, this.productVersion, this.commit, System.getProperty("os.name")));
Request request = chain.request().newBuilder().header(USER_AGENT,String.format("%s/%s (commit:%s;os:%s)",
this.productName, this.productVersion, this.commit, System.getProperty("os.name"))).build();
return ex.execute(req, reqBody);
return chain.proceed(request);
}
}
......@@ -15,18 +15,18 @@ import dev.fitko.fitconnect.api.services.auth.OAuthService;
import dev.fitko.fitconnect.api.services.keys.KeyService;
import dev.fitko.fitconnect.api.services.submission.SubmissionService;
import dev.fitko.fitconnect.api.services.validation.ValidationService;
import dev.fitko.fitconnect.core.http.HttpClient;
import dev.fitko.fitconnect.core.http.HttpHeaders;
import dev.fitko.fitconnect.core.http.MimeTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class PublicKeyService implements KeyService {
......@@ -38,17 +38,17 @@ public class PublicKeyService implements KeyService {
private final ApplicationConfig config;
private final ValidationService validationService;
private final SubmissionService submissionService;
private final RestTemplate restTemplate;
private final HttpClient httpClient;
private final OAuthService authService;
public PublicKeyService(
final ApplicationConfig config,
final RestTemplate restTemplate,
final HttpClient httpClient,
final OAuthService authService,
final SubmissionService submissionService,
final ValidationService validationService) {
this.config = config;
this.restTemplate = restTemplate;
this.httpClient = httpClient;
this.authService = authService;
this.submissionService = submissionService;
this.validationService = validationService;
......@@ -90,10 +90,11 @@ public class PublicKeyService implements KeyService {
validateSignatureKey(signatureKey);
return signatureKey;
}
@Override
public RSAKey getWellKnownKeysForSubmissionUrl(final String url, final String keyId) {
final var requestUrl = !url.endsWith("/") ? url + config.getWellKnownKeysPath() : url;
final ApiJwkSet wellKnownKeys = performRequest(requestUrl, ApiJwkSet.class, getHeadersWithoutAuth(), keyId);
final ApiJwkSet wellKnownKeys = performRequest(requestUrl, ApiJwkSet.class, getHeadersWithoutAuth(), keyId);
final RSAKey signatureKey = filterKeysById(keyId, wellKnownKeys.getKeys());
validateSignatureKey(signatureKey);
return signatureKey;
......@@ -135,24 +136,26 @@ public class PublicKeyService implements KeyService {
}
}
private <T> T performRequest(final String url, final Class<T> responseType, final HttpHeaders headers, final Object... params) {
final HttpEntity<String> entity = new HttpEntity<>(headers);
private <T> T performRequest(final String url, final Class<T> responseType, final Map<String, String> headers, final Object... params) {
try {
return restTemplate.exchange(url, HttpMethod.GET, entity, responseType, params).getBody();
} catch (final RestClientException e) {
return this.httpClient.get(String.format(url, params), headers, responseType).getBody();
} catch (final IOException e) {
throw new RestApiException("Request failed", e);
}
}
private HttpHeaders getHeadersWithoutAuth() {
final var headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAcceptCharset(List.of(StandardCharsets.UTF_8));
return headers;
private Map<String, String> getHeadersWithoutAuth() {
return new HashMap<>(Map.of(
HttpHeaders.CONTENT_TYPE, MimeTypes.APPLICATION_JSON,
HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.toString()));
}
private HttpHeaders getHeaders() {
private Map<String, String> getHeaders() {
final var headers = getHeadersWithoutAuth();
headers.setBearerAuth(authService.getCurrentToken().getAccessToken());
getHeadersWithoutAuth().put(HttpHeaders.AUTHORIZATION, "Bearer: " + authService.getCurrentToken().getAccessToken());
return headers;
}
}
......@@ -5,46 +5,45 @@ import dev.fitko.fitconnect.api.domain.model.route.AreaResult;
import dev.fitko.fitconnect.api.domain.model.route.RouteResult;
import dev.fitko.fitconnect.api.exceptions.RestApiException;
import dev.fitko.fitconnect.api.services.routing.RoutingService;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import dev.fitko.fitconnect.core.http.HttpClient;
import dev.fitko.fitconnect.core.http.HttpHeaders;
import dev.fitko.fitconnect.core.http.MimeTypes;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import static java.util.function.Predicate.not;
public class RoutingApiService implements RoutingService {
private final RestTemplate restTemplate;
private final HttpClient httpClient;
private final ApplicationConfig config;
public RoutingApiService(final ApplicationConfig config, final RestTemplate restTemplate) {
public RoutingApiService(final ApplicationConfig config, final HttpClient httpClient) {
this.config = config;
this.restTemplate = restTemplate;
this.httpClient = httpClient;
}
@Override
public AreaResult getAreas(final List<String> searchExpressions, final int offset, final int limit) {
final String url = config.getAreaEndpoint();
final HttpHeaders headers = getHttpHeaders(MediaType.parseMediaType("application/problem+json"));
final HttpEntity<String> entity = new HttpEntity<>(headers);
final UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(url);
searchExpressions.forEach(search -> uriBuilder.queryParam("areaSearchexpression", search));
uriBuilder.queryParam("offset", offset);
uriBuilder.queryParam("limit", limit);
List<String> queryParameters = new ArrayList<>();
searchExpressions.forEach(search -> queryParameters.add("areaSearchexpression=" + search));
queryParameters.add("offset=" + offset);
queryParameters.add("limit=" + limit);
String queryparameterString = String.join("&", queryParameters);
try {
return restTemplate.exchange(uriBuilder.toUriString(), HttpMethod.GET, entity, AreaResult.class).getBody();
} catch (final RestClientException e) {
return this.httpClient.get(url + "?" + queryparameterString, getHttpHeaders(MimeTypes.APPLICATION_PROBLEM_JSON), AreaResult.class).getBody();
} catch (final IOException e) {
throw new RestApiException("Area query failed", e);
}
}
......@@ -55,28 +54,24 @@ public class RoutingApiService implements RoutingService {
testIfSearchCriterionIsSet(ars, ags, areaId);
final String url = config.getRoutesEndpoint();
final HttpHeaders headers = getHttpHeaders(MediaType.APPLICATION_JSON);
final HttpEntity<String> entity = new HttpEntity<>(headers);
final UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(url);
uriBuilder.queryParam("leikaKey", leikaKey);
List<String> queryParameters = new ArrayList<>();
if (ars != null) {
uriBuilder.queryParam("ars", ars);
queryParameters.add("ars=" + ars);
}
if (ags != null) {
uriBuilder.queryParam("ags", ags);
queryParameters.add("ags=" + ags);
}
if (areaId != null) {
uriBuilder.queryParam("areaId", areaId);
queryParameters.add("areaId=" + areaId);
}
uriBuilder.queryParam("offset", offset);
uriBuilder.queryParam("limit", limit);
queryParameters.add("offset=" + offset);
queryParameters.add("limit=" + limit);
String queryparameterString = String.join("&", queryParameters);
try {
return restTemplate.exchange(uriBuilder.toUriString(), HttpMethod.GET, entity, RouteResult.class).getBody();
} catch (final RestClientException e) {
return this.httpClient.get(url + "?" + queryparameterString, getHttpHeaders(MimeTypes.APPLICATION_JSON), RouteResult.class).getBody();
} catch (final IOException e) {
throw new RestApiException("Route query failed", e);
}
}
......@@ -84,11 +79,11 @@ public class RoutingApiService implements RoutingService {
private static void testIfSearchCriterionIsSet(final String ars, final String ags, final String areaId) {
final List<String> areaSearchCriteria = Arrays.asList(ars, ags, areaId);
if(areaSearchCriteria.stream().allMatch(CriterionEmpty())){
if (areaSearchCriteria.stream().allMatch(CriterionEmpty())) {
throw new RestApiException("At least one search criterion out of ags, ars or areaId must be set");
}
if(areaSearchCriteria.stream().filter(not(CriterionEmpty())).count() > 1){
if (areaSearchCriteria.stream().filter(not(CriterionEmpty())).count() > 1) {
throw new RestApiException("Only one of ars, ags or areaId must be specified.");
}
}
......@@ -97,10 +92,11 @@ public class RoutingApiService implements RoutingService {
return criterion -> criterion == null || criterion.isEmpty();
}
private HttpHeaders getHttpHeaders(final MediaType mediaType) {
final var headers = new HttpHeaders();
headers.setAccept(List.of(mediaType));
headers.setAcceptCharset(List.of(StandardCharsets.UTF_8));
return headers;
private Map<String, String> getHttpHeaders(final String mediaType) {
return new HashMap<>(Map.of(
HttpHeaders.ACCEPT, mediaType,
HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.toString()
));
}
}
......@@ -7,9 +7,9 @@ import dev.fitko.fitconnect.api.domain.schema.SchemaResources;
import dev.fitko.fitconnect.api.exceptions.InitializationException;
import dev.fitko.fitconnect.api.exceptions.SchemaNotFoundException;
import dev.fitko.fitconnect.api.services.schema.SchemaProvider;
import dev.fitko.fitconnect.core.http.HttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.client.RestTemplate;
import java.io.BufferedReader;
import java.io.File;
......@@ -39,16 +39,16 @@ public class SchemaResourceProvider implements SchemaProvider {
private final Map<URI, String> submissionDataSchemas;
private final RestTemplate restTemplate;
private final HttpClient httpClient;
private static final ObjectMapper MAPPER = new ObjectMapper();
public SchemaResourceProvider(final RestTemplate restTemplate, final SchemaResources schemaResources) {
public SchemaResourceProvider(final HttpClient httpClient, final SchemaResources schemaResources) {
setSchemas = new HashMap<>();
metadataSchemas = new HashMap<>();
destinationSchemas = new HashMap<>();
submissionDataSchemas = new HashMap<>();
this.restTemplate = restTemplate;
this.httpClient = httpClient;
populateSetSchemas(schemaResources.getSetSchemaPaths());
populateMetadataSchemas(schemaResources.getMetadataSchemaPaths());
......@@ -120,7 +120,7 @@ public class SchemaResourceProvider implements SchemaProvider {
if (schemaUri.toString().matches("http.+")) {
try {
return restTemplate.getForEntity(schemaUri, String.class).getBody();
return this.httpClient.get(schemaUri.toString(), String.class).getBody();
} catch (Exception exception) {
LOGGER.warn("Fetching schema from " + schemaUri + " failed, falling back to pre-stored version.");
}
......
package dev.fitko.fitconnect.core.submission;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.fitko.fitconnect.api.config.ApplicationConfig;
import dev.fitko.fitconnect.api.domain.auth.OAuthToken;
import dev.fitko.fitconnect.api.domain.model.destination.Destination;
import dev.fitko.fitconnect.api.domain.model.submission.CreateSubmission;
import dev.fitko.fitconnect.api.domain.model.submission.Submission;
......@@ -13,214 +10,132 @@ import dev.fitko.fitconnect.api.domain.model.submission.SubmitSubmission;
import dev.fitko.fitconnect.api.exceptions.RestApiException;
import dev.fitko.fitconnect.api.services.auth.OAuthService;
import dev.fitko.fitconnect.api.services.submission.SubmissionService;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import dev.fitko.fitconnect.core.http.HttpClient;
import dev.fitko.fitconnect.core.http.HttpHeaders;
import dev.fitko.fitconnect.core.http.MimeTypes;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
public class SubmissionApiService implements SubmissionService {
private final OAuthService authService;
private final RestTemplate restTemplate;
private final HttpClient httpClient;
private final ApplicationConfig config;
public SubmissionApiService(final OAuthService authService, final RestTemplate restTemplate, final ApplicationConfig config) {
public SubmissionApiService(final OAuthService authService, final HttpClient httpClient, final ApplicationConfig config) {
this.authService = authService;
this.restTemplate = restTemplate;
this.httpClient = httpClient;
this.config = config;
}
@Override
public Destination getDestination(final UUID destinationID) throws RestApiException {
final RequestSettings requestSettings = RequestSettings.builder()
.url(config.getDestinationsEndpoint())
.method(HttpMethod.GET)
.responseType(Destination.class)
.entity(getHttpEntity(getHeaders()))
.params(Map.of("destinationId", destinationID))
.errorMessage("Could not get destination " + destinationID)
.build();
return performRequest(requestSettings);
final String url = String.format(config.getDestinationsEndpoint(), destinationID);
try {
return this.httpClient.get(url, getHeaders(), Destination.class).getBody();
} catch (IOException e) {
throw new RestApiException("Could not get destination " + destinationID, e);
}
}
@Override
public SubmissionForPickup announceSubmission(final CreateSubmission submission) throws RestApiException {
final RequestSettings requestSettings = RequestSettings.builder()
.url(config.getSubmissionsEndpoint())
.method(HttpMethod.POST)
.responseType(SubmissionForPickup.class)
.entity(getHttpEntity(submission, getHeaders()))
.params(Collections.emptyMap())
.errorMessage("Could not announce submission for destination " + submission.getDestinationId())
.build();
return performRequest(requestSettings);
try {
return this.httpClient.post(config.getSubmissionsEndpoint(), getHeaders(), submission, SubmissionForPickup.class).getBody();
} catch (IOException e) {
throw new RestApiException("Could not announce submission for destination " + submission.getDestinationId(), e);
}
}
@Override
public void uploadAttachment(final UUID submissionId, final UUID attachmentId, final String encryptedAttachment) throws RestApiException {
final Map<String, Object> params = new HashMap<>();
params.put("submissionId", submissionId);
params.put("attachmentId", attachmentId);
final RequestSettings requestSettings = RequestSettings.builder()
.url(config.getAttachmentEndpoint())
.method(HttpMethod.PUT)
.responseType(Void.class)
.entity(getHttpEntity(encryptedAttachment, getHeaders("application/jose")))
.params(params)
.errorMessage("Could not upload attachment")
.build();
performRequest(requestSettings);
final String url = String.format(config.getAttachmentEndpoint(), submissionId, attachmentId);
try {
this.httpClient.put(url, getHeaders("application/jose"), encryptedAttachment, Void.class);
} catch (IOException e) {
throw new RestApiException("Could not upload attachment", e);
}
}
@Override
public String getAttachment(final UUID submissionId, final UUID attachmentId) throws RestApiException {
final Map<String, Object> params = new HashMap<>();
params.put("submissionId", submissionId);
params.put("attachmentId", attachmentId);
final RequestSettings requestSettings = RequestSettings.builder()
.url(config.getAttachmentEndpoint())
.method(HttpMethod.GET)
.responseType(String.class)
.entity(getHttpEntity(getHeaders("application/jose")))
.params(params)
.errorMessage("Could not get attachment " + attachmentId)
.build();
return performRequest(requestSettings);
final String url = String.format(config.getAttachmentEndpoint(), submissionId, attachmentId);
try {
return this.httpClient.get(url, getHeaders("application/jose"), String.class).getBody();
} catch (IOException e) {
throw new RestApiException("Could not get attachment " + attachmentId, e);
}
}
@Override
public Submission sendSubmission(final SubmitSubmission submission) throws RestApiException {
final Map<String, Object> params = Map.of("submissionId", submission.getSubmissionId());
final RequestSettings requestSettings = RequestSettings.builder()
.url(config.getSubmissionEndpoint())
.method(HttpMethod.PUT)
.responseType(Submission.class)
.entity(getHttpEntity(submission, getHeaders()))
.params(params)
.errorMessage("Could not send submission " + submission.getSubmissionId())
.build();
return performRequest(requestSettings);
final String url = String.format(config.getSubmissionEndpoint(), submission);
try {
return this.httpClient.put(url, getHeaders(), submission, Submission.class).getBody();
} catch (IOException e) {
throw new RestApiException("Could not send submission " + submission.getSubmissionId(), e);
}
}
@Override
public SubmissionsForPickup pollAvailableSubmissions(final int offset, final int limit) throws RestApiException {
final String urlWithQueryParams = UriComponentsBuilder.fromHttpUrl(config.getSubmissionsEndpoint())
.queryParam("limit", limit)
.queryParam("offset", offset)
.encode()
.toUriString();
final RequestSettings requestSettings = RequestSettings.builder()
.url(urlWithQueryParams)
.method(HttpMethod.GET)
.responseType(SubmissionsForPickup.class)
.entity(getHttpEntity(getHeaders()))
.errorMessage("Could not poll for available submissions")
.build();
return performRequest(requestSettings);
final String urlWithQueryParams = config.getSubmissionsEndpoint() + "?limit=" + limit + "&offset=" + offset;
try {
return this.httpClient.get(urlWithQueryParams, getHeaders(), SubmissionsForPickup.class).getBody();
} catch (IOException e) {
throw new RestApiException("Could not poll for available submissions", e);
}
}
@Override
public SubmissionsForPickup pollAvailableSubmissionsForDestination(final UUID destinationId, final int offset, final int limit) {
final String urlWithQueryParams = UriComponentsBuilder.fromHttpUrl(config.getSubmissionsEndpoint())
.queryParam("destinationId", destinationId)
.queryParam("limit", limit)
.queryParam("offset", offset)
.encode()
.toUriString();
final RequestSettings requestSettings = RequestSettings.builder()
.url(urlWithQueryParams)
.method(HttpMethod.GET)
.responseType(SubmissionsForPickup.class)
.entity(getHttpEntity(getHeaders()))
.errorMessage("Could not poll for available submissions on destination " + destinationId)
.build();
return performRequest(requestSettings);
}
@Override
public Submission getSubmission(final UUID submissionId) throws RestApiException {
final Map<String, Object> params = Map.of("submissionId", submissionId);
final RequestSettings requestSettings = RequestSettings.builder()
.url(config.getSubmissionEndpoint())
.method(HttpMethod.GET)
.responseType(Submission.class)
.entity(getHttpEntity(getHeaders()))
.params(params)
.errorMessage("Submission with id " + submissionId + " does not exist")
.build();
return performRequest(requestSettings);
}
final String urlWithQueryParams = config.getSubmissionsEndpoint() + "?destinationId=" + destinationId
+ "&limit=" + limit + "&offset=" + offset;
private <T> T performRequest(final RequestSettings requestSettings) throws RestApiException {
final String url = requestSettings.url;
final HttpMethod method = requestSettings.method;
final HttpEntity entity = requestSettings.entity;
final Class<T> responseType = requestSettings.responseType;
final Map<String, ?> params = requestSettings.params;
try {
return restTemplate.exchange(url, method, entity, responseType, params).getBody();
} catch (final RestClientException e) {
throw new RestApiException(e.getMessage(), e);
return this.httpClient.get(urlWithQueryParams, getHeaders(), SubmissionsForPickup.class).getBody();
} catch (IOException e) {
throw new RestApiException("Could not poll for available submissions on destination " + destinationId, e);
}
}
private HttpEntity<String> getHttpEntity(final HttpHeaders headers) {
return getHttpEntity(null, headers);
}
@Override
public Submission getSubmission(final UUID submissionId) throws RestApiException {
private HttpEntity<String> getHttpEntity(final Object body, final HttpHeaders headers) {
if (body == null) {
return new HttpEntity<>(headers);
}
try {
return getHttpEntity(new ObjectMapper().writeValueAsString(body), headers);
} catch (final JsonProcessingException e) {
throw new RestApiException("Request body could not be processed", e);
return this.httpClient.get(String.format(config.getSubmissionEndpoint(), submissionId), getHeaders(), Submission.class).getBody();
} catch (IOException e) {
throw new RestApiException("Submission with id " + submissionId + " does not exist", e);
}
}
private HttpEntity<String> getHttpEntity(final String body, final HttpHeaders headers) {
return body == null ? new HttpEntity<>(headers) : new HttpEntity<>(body, headers);
}
private HttpHeaders getHeaders() {
return getHeaders(APPLICATION_JSON_VALUE);
private Map<String, String> getHeaders() {
return getHeaders(MimeTypes.APPLICATION_JSON);
}
private HttpHeaders getHeaders(final String contentType) {
final OAuthToken token = authService.getCurrentToken();
final var headers = new HttpHeaders();
headers.setBearerAuth(token.getAccessToken());
headers.setContentType(MediaType.parseMediaType(contentType));
headers.setAcceptCharset(List.of(StandardCharsets.UTF_8));
return headers;
}
private Map<String, String> getHeaders(final String contentType) {
@Getter
@Setter
@Builder
private static class RequestSettings {
private String url;
private HttpMethod method;
private HttpEntity entity;
private Class responseType;
private String errorMessage;
@Builder.Default
private Map<String, Object> params = new HashMap<>();
return new HashMap<>(Map.of(
HttpHeaders.AUTHORIZATION, "Bearer: " + authService.getCurrentToken(),
HttpHeaders.CONTENT_TYPE, contentType,
HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.toString()
));
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment